diff options
Diffstat (limited to 'src')
128 files changed, 20473 insertions, 13627 deletions
diff --git a/src/Makefile.am b/src/Makefile.am index 7e9fc9e..367f5d9 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,31 +1,60 @@ +AM_CFLAGS = $(FAM_CFLAGS) + noinst_PROGRAMS=proc_open lemon # simple-fcgi #graphic evalo bench ajp ssl error_test adserver gen-license -sbin_PROGRAMS=lighttpd -bin_PROGRAMS=spawn-fcgi -LEMON=$(top_builddir)/src/lemon +sbin_PROGRAMS=lighttpd lighttpd-angel +LEMON=$(top_builddir)/src/lemon$(EXEEXT) lemon_SOURCES=lemon.c -#simple_fcgi_SOURCES=simple-fcgi.c -#simple_fcgi_LDADD=-lfcgi +lighttpd_angel_SOURCES=lighttpd-angel.c + +.PHONY: versionstamp parsers + +versionstamp: + @test -f versionstamp.h || touch versionstamp.h; \ + REVISION=""; \ + if test -d "$(top_srcdir)/.svn" -a -x "`which svnversion`"; then \ + REVISION="$$(LANG= LC_ALL=C svnversion "$(top_srcdir)" 2>/dev/null || echo exported)"; \ + if test "$$REVISION" = "exported"; then \ + REVISION=""; \ + fi; \ + fi; \ + if test -z "$$REVISION" -a -d "$(top_srcdir)/.git" -a -x "`which git`"; then \ + REVISION="$$(cd "$(top_srcdir)"; LANG= LC_ALL=C git describe --always 2>/dev/null || echo)"; \ + fi; \ + if test -n "$$REVISION"; then \ + echo "#define REPO_VERSION \"-devel-$$REVISION\"" > versionstamp.h.tmp; \ + else \ + echo "#define REPO_VERSION \"\"" > versionstamp.h.tmp; \ + fi; \ + if ! diff versionstamp.h.tmp versionstamp.h >/dev/null 2>/dev/null; then \ + mv versionstamp.h.tmp versionstamp.h; \ + else \ + rm versionstamp.h.tmp; \ + fi if CROSS_COMPILING -configparser.c configparser.h: -mod_ssi_exprparser.c mod_ssi_exprparser.h: -else -configparser.y: lemon -mod_ssi_exprparser.y: lemon +configparser.c configparser.h: +mod_ssi_exprparser.c mod_ssi_exprparser.h: -configparser.c configparser.h: configparser.y +parsers: +else +configparser.h: configparser.c +configparser.c: $(srcdir)/configparser.y $(srcdir)/lempar.c lemon$(EXEEXT) rm -f configparser.h $(LEMON) -q $(srcdir)/configparser.y $(srcdir)/lempar.c -mod_ssi_exprparser.c mod_ssi_exprparser.h: mod_ssi_exprparser.y +mod_ssi_exprparser.h: mod_ssi_exprparser.c +mod_ssi_exprparser.c: $(srcdir)/mod_ssi_exprparser.y $(srcdir)/lempar.c lemon$(EXEEXT) rm -f mod_ssi_exprparser.h $(LEMON) -q $(srcdir)/mod_ssi_exprparser.y $(srcdir)/lempar.c + +parsers: configparser.c mod_ssi_exprparser.c endif -configfile.c: configparser.h -mod_ssi_expr.c: mod_ssi_exprparser.h +BUILT_SOURCES = parsers versionstamp +MAINTAINERCLEANFILES = configparser.c configparser.h mod_ssi_exprparser.c mod_ssi_exprparser.h +CLEANFILES = versionstamp.h versionstamp.h.tmp common_src=buffer.c log.c \ keyvalue.c chunk.c \ @@ -33,9 +62,10 @@ common_src=buffer.c log.c \ stat_cache.c plugin.c joblist.c etag.c array.c \ data_string.c data_count.c data_array.c \ data_integer.c md5.c data_fastcgi.c \ - fdevent_select.c fdevent_linux_rtsig.c \ + fdevent_select.c fdevent_libev.c \ fdevent_poll.c fdevent_linux_sysepoll.c \ - fdevent_solaris_devpoll.c fdevent_freebsd_kqueue.c \ + fdevent_solaris_devpoll.c fdevent_solaris_port.c \ + fdevent_freebsd_kqueue.c \ data_config.c bitset.c \ inet_ntop_cache.c crc32.c \ connections-glue.c \ @@ -44,14 +74,12 @@ common_src=buffer.c log.c \ network_write.c network_linux_sendfile.c \ network_freebsd_sendfile.c network_writev.c \ network_solaris_sendfilev.c network_openssl.c \ - splaytree.c - + splaytree.c status_counter.c + src = server.c response.c connections.c network.c \ configfile.c configparser.c request.c proc_open.c -spawn_fcgi_SOURCES=spawn-fcgi.c - -lib_LTLIBRARIES = +lib_LTLIBRARIES = if NO_RDYNAMIC # if the linker doesn't allow referencing symbols of the binary @@ -59,26 +87,36 @@ if NO_RDYNAMIC # everything lib_LTLIBRARIES += liblightcomp.la liblightcomp_la_SOURCES=$(common_src) -liblightcomp_la_CFLAGS=$(AM_CFLAGS) $(FAM_CFLAGS) +liblightcomp_la_CFLAGS=$(AM_CFLAGS) $(LIBEV_CFLAGS) liblightcomp_la_LDFLAGS = -avoid-version -no-undefined -liblightcomp_la_LIBADD = $(PCRE_LIB) $(SSL_LIB) $(FAM_LIBS) +liblightcomp_la_LIBADD = $(PCRE_LIB) $(SSL_LIB) $(FAM_LIBS) $(LIBEV_LIBS) common_libadd = liblightcomp.la else src += $(common_src) -common_libadd = +common_libadd = endif +lib_LTLIBRARIES += mod_flv_streaming.la +mod_flv_streaming_la_SOURCES = mod_flv_streaming.c +mod_flv_streaming_la_LDFLAGS = -module -export-dynamic -avoid-version -no-undefined +mod_flv_streaming_la_LIBADD = $(common_libadd) + lib_LTLIBRARIES += mod_evasive.la mod_evasive_la_SOURCES = mod_evasive.c mod_evasive_la_LDFLAGS = -module -export-dynamic -avoid-version -no-undefined mod_evasive_la_LIBADD = $(common_libadd) - lib_LTLIBRARIES += mod_webdav.la mod_webdav_la_SOURCES = mod_webdav.c -mod_webdav_la_CFLAGS = $(XML_CFLAGS) +mod_webdav_la_CFLAGS = $(AM_CFLAGS) $(XML_CFLAGS) $(SQLITE_CFLAGS) mod_webdav_la_LDFLAGS = -module -export-dynamic -avoid-version -no-undefined -mod_webdav_la_LIBADD = $(common_libadd) $(XML_LIBS) $(SQLITE_LIBS) +mod_webdav_la_LIBADD = $(common_libadd) $(XML_LIBS) $(SQLITE_LIBS) $(UUID_LIBS) + +lib_LTLIBRARIES += mod_magnet.la +mod_magnet_la_SOURCES = mod_magnet.c mod_magnet_cache.c +mod_magnet_la_CFLAGS = $(AM_CFLAGS) $(LUA_CFLAGS) +mod_magnet_la_LDFLAGS = -module -export-dynamic -avoid-version -no-undefined +mod_magnet_la_LIBADD = $(common_libadd) $(LUA_LIBS) -lm lib_LTLIBRARIES += mod_cml.la mod_cml_la_SOURCES = mod_cml.c mod_cml_lua.c mod_cml_funcs.c @@ -98,42 +136,42 @@ mod_mysql_vhost_la_LIBADD = $(MYSQL_LIBS) $(common_libadd) mod_mysql_vhost_la_CPPFLAGS = $(MYSQL_INCLUDE) lib_LTLIBRARIES += mod_cgi.la -mod_cgi_la_SOURCES = mod_cgi.c +mod_cgi_la_SOURCES = mod_cgi.c mod_cgi_la_LDFLAGS = -module -export-dynamic -avoid-version -no-undefined mod_cgi_la_LIBADD = $(common_libadd) lib_LTLIBRARIES += mod_scgi.la -mod_scgi_la_SOURCES = mod_scgi.c +mod_scgi_la_SOURCES = mod_scgi.c mod_scgi_la_LDFLAGS = -module -export-dynamic -avoid-version -no-undefined mod_scgi_la_LIBADD = $(common_libadd) lib_LTLIBRARIES += mod_staticfile.la -mod_staticfile_la_SOURCES = mod_staticfile.c +mod_staticfile_la_SOURCES = mod_staticfile.c mod_staticfile_la_LDFLAGS = -module -export-dynamic -avoid-version -no-undefined mod_staticfile_la_LIBADD = $(common_libadd) lib_LTLIBRARIES += mod_dirlisting.la -mod_dirlisting_la_SOURCES = mod_dirlisting.c +mod_dirlisting_la_SOURCES = mod_dirlisting.c mod_dirlisting_la_LDFLAGS = -module -export-dynamic -avoid-version -no-undefined mod_dirlisting_la_LIBADD = $(common_libadd) $(PCRE_LIB) lib_LTLIBRARIES += mod_indexfile.la -mod_indexfile_la_SOURCES = mod_indexfile.c +mod_indexfile_la_SOURCES = mod_indexfile.c mod_indexfile_la_LDFLAGS = -module -export-dynamic -avoid-version -no-undefined mod_indexfile_la_LIBADD = $(common_libadd) lib_LTLIBRARIES += mod_setenv.la -mod_setenv_la_SOURCES = mod_setenv.c +mod_setenv_la_SOURCES = mod_setenv.c mod_setenv_la_LDFLAGS = -module -export-dynamic -avoid-version -no-undefined mod_setenv_la_LIBADD = $(common_libadd) lib_LTLIBRARIES += mod_alias.la -mod_alias_la_SOURCES = mod_alias.c +mod_alias_la_SOURCES = mod_alias.c mod_alias_la_LDFLAGS = -module -export-dynamic -avoid-version -no-undefined mod_alias_la_LIBADD = $(common_libadd) lib_LTLIBRARIES += mod_userdir.la -mod_userdir_la_SOURCES = mod_userdir.c +mod_userdir_la_SOURCES = mod_userdir.c mod_userdir_la_LDFLAGS = -module -export-dynamic -avoid-version -no-undefined mod_userdir_la_LIBADD = $(common_libadd) @@ -153,7 +191,7 @@ mod_proxy_la_LDFLAGS = -module -export-dynamic -avoid-version -no-undefined mod_proxy_la_LIBADD = $(common_libadd) lib_LTLIBRARIES += mod_ssi.la -mod_ssi_la_SOURCES = mod_ssi_exprparser.c mod_ssi_expr.c mod_ssi.c +mod_ssi_la_SOURCES = mod_ssi_exprparser.c mod_ssi_expr.c mod_ssi.c mod_ssi_la_LDFLAGS = -module -export-dynamic -avoid-version -no-undefined mod_ssi_la_LIBADD = $(common_libadd) $(PCRE_LIB) @@ -181,24 +219,29 @@ lib_LTLIBRARIES += mod_simple_vhost.la mod_simple_vhost_la_SOURCES = mod_simple_vhost.c mod_simple_vhost_la_LDFLAGS = -module -export-dynamic -avoid-version -no-undefined mod_simple_vhost_la_LIBADD = $(common_libadd) - + lib_LTLIBRARIES += mod_fastcgi.la mod_fastcgi_la_SOURCES = mod_fastcgi.c mod_fastcgi_la_LDFLAGS = -module -export-dynamic -avoid-version -no-undefined mod_fastcgi_la_LIBADD = $(common_libadd) +lib_LTLIBRARIES += mod_extforward.la +mod_extforward_la_SOURCES = mod_extforward.c +mod_extforward_la_LDFLAGS = -module -export-dynamic -avoid-version -no-undefined +mod_extforward_la_LIBADD = $(common_libadd) + lib_LTLIBRARIES += mod_access.la mod_access_la_SOURCES = mod_access.c mod_access_la_LDFLAGS = -module -export-dynamic -avoid-version -no-undefined mod_access_la_LIBADD = $(common_libadd) lib_LTLIBRARIES += mod_compress.la -mod_compress_la_SOURCES = mod_compress.c +mod_compress_la_SOURCES = mod_compress.c mod_compress_la_LDFLAGS = -module -export-dynamic -avoid-version -no-undefined mod_compress_la_LIBADD = $(Z_LIB) $(BZ_LIB) $(common_libadd) lib_LTLIBRARIES += mod_auth.la -mod_auth_la_SOURCES = mod_auth.c http_auth_digest.c http_auth.c +mod_auth_la_SOURCES = mod_auth.c http_auth.c mod_auth_la_LDFLAGS = -module -export-dynamic -avoid-version -no-undefined mod_auth_la_LIBADD = $(CRYPT_LIB) $(LDAP_LIB) $(LBER_LIB) $(common_libadd) @@ -225,7 +268,7 @@ mod_accesslog_la_LIBADD = $(common_libadd) hdr = server.h buffer.h network.h log.h keyvalue.h \ response.h request.h fastcgi.h chunk.h \ - settings.h http_chunk.h http_auth_digest.h \ + settings.h http_chunk.h \ md5.h http_auth.h stream.h \ fdevent.h connections.h base.h stat_cache.h \ plugin.h mod_auth.h \ @@ -234,14 +277,16 @@ hdr = server.h buffer.h network.h log.h keyvalue.h \ mod_ssi.h mod_ssi_expr.h inet_ntop_cache.h \ configparser.h mod_ssi_exprparser.h \ sys-mmap.h sys-socket.h mod_cml.h mod_cml_funcs.h \ - splaytree.h proc_open.h + splaytree.h proc_open.h status_counter.h \ + mod_magnet_cache.h \ + version.h -DEFS= @DEFS@ -DLIBRARY_DIR="\"$(libdir)\"" +DEFS= @DEFS@ -DHAVE_VERSION_H -DLIBRARY_DIR="\"$(libdir)\"" -DSBIN_DIR="\"$(sbindir)\"" lighttpd_SOURCES = $(src) -lighttpd_LDADD = $(PCRE_LIB) $(DL_LIB) $(SENDFILE_LIB) $(ATTR_LIB) $(common_libadd) $(SSL_LIB) $(FAM_LIBS) +lighttpd_LDADD = $(PCRE_LIB) $(DL_LIB) $(SENDFILE_LIB) $(ATTR_LIB) $(common_libadd) $(SSL_LIB) $(FAM_LIBS) $(LIBEV_LIBS) lighttpd_LDFLAGS = -export-dynamic -lighttpd_CCPFLAGS = $(FAM_CFLAGS) +lighttpd_CCPFLAGS = $(FAM_CFLAGS) $(LIBEV_CFLAGS) proc_open_SOURCES = proc_open.c buffer.c proc_open_CPPFLAGS= -DDEBUG_PROC_OPEN @@ -261,4 +306,5 @@ proc_open_CPPFLAGS= -DDEBUG_PROC_OPEN #ajp_SOURCES = ajp.c noinst_HEADERS = $(hdr) -EXTRA_DIST = mod_skeleton.c configparser.y mod_ssi_exprparser.y lempar.c +EXTRA_DIST = mod_skeleton.c configparser.y mod_ssi_exprparser.y lempar.c SConscript + diff --git a/src/Makefile.in b/src/Makefile.in index cc6fab9..705e7ef 100644 --- a/src/Makefile.in +++ b/src/Makefile.in @@ -1,8 +1,9 @@ -# Makefile.in generated by automake 1.9.5 from Makefile.am. +# Makefile.in generated by automake 1.11.6 from Makefile.am. # @configure_input@ # Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, -# 2003, 2004, 2005 Free Software Foundation, Inc. +# 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 Free Software +# Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. @@ -16,17 +17,29 @@ -SOURCES = $(liblightcomp_la_SOURCES) $(mod_access_la_SOURCES) $(mod_accesslog_la_SOURCES) $(mod_alias_la_SOURCES) $(mod_auth_la_SOURCES) $(mod_cgi_la_SOURCES) $(mod_cml_la_SOURCES) $(mod_compress_la_SOURCES) $(mod_dirlisting_la_SOURCES) $(mod_evasive_la_SOURCES) $(mod_evhost_la_SOURCES) $(mod_expire_la_SOURCES) $(mod_fastcgi_la_SOURCES) $(mod_indexfile_la_SOURCES) $(mod_mysql_vhost_la_SOURCES) $(mod_proxy_la_SOURCES) $(mod_redirect_la_SOURCES) $(mod_rewrite_la_SOURCES) $(mod_rrdtool_la_SOURCES) $(mod_scgi_la_SOURCES) $(mod_secdownload_la_SOURCES) $(mod_setenv_la_SOURCES) $(mod_simple_vhost_la_SOURCES) $(mod_ssi_la_SOURCES) $(mod_staticfile_la_SOURCES) $(mod_status_la_SOURCES) $(mod_trigger_b4_dl_la_SOURCES) $(mod_userdir_la_SOURCES) $(mod_usertrack_la_SOURCES) $(mod_webdav_la_SOURCES) $(lemon_SOURCES) $(lighttpd_SOURCES) $(proc_open_SOURCES) $(spawn_fcgi_SOURCES) - -srcdir = @srcdir@ -top_srcdir = @top_srcdir@ VPATH = @srcdir@ +am__make_dryrun = \ + { \ + am__dry=no; \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + echo 'am--echo: ; @echo "AM" OK' | $(MAKE) -f - 2>/dev/null \ + | grep '^AM OK$$' >/dev/null || am__dry=yes;; \ + *) \ + for am__flg in $$MAKEFLAGS; do \ + case $$am__flg in \ + *=*|--*) ;; \ + *n*) am__dry=yes; break;; \ + esac; \ + done;; \ + esac; \ + test $$am__dry = yes; \ + } pkgdatadir = $(datadir)/@PACKAGE@ -pkglibdir = $(libdir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ -top_builddir = .. +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd -INSTALL = @INSTALL@ install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c @@ -42,8 +55,7 @@ build_triplet = @build@ host_triplet = @host@ target_triplet = @target@ noinst_PROGRAMS = proc_open$(EXEEXT) lemon$(EXEEXT) -sbin_PROGRAMS = lighttpd$(EXEEXT) -bin_PROGRAMS = spawn-fcgi$(EXEEXT) +sbin_PROGRAMS = lighttpd$(EXEEXT) lighttpd-angel$(EXEEXT) # if the linker doesn't allow referencing symbols of the binary # we have to put everything into a shared-lib and link it into @@ -54,37 +66,61 @@ subdir = src DIST_COMMON = $(noinst_HEADERS) $(srcdir)/Makefile.am \ $(srcdir)/Makefile.in ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 -am__aclocal_m4_deps = $(top_srcdir)/configure.in +am__aclocal_m4_deps = $(top_srcdir)/m4/libtool.m4 \ + $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ + $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ + $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) -mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs +mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; -am__strip_dir = `echo $$p | sed -e 's|^.*/||'`; -am__installdirs = "$(DESTDIR)$(libdir)" "$(DESTDIR)$(bindir)" \ - "$(DESTDIR)$(sbindir)" -libLTLIBRARIES_INSTALL = $(INSTALL) +am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; +am__install_max = 40 +am__nobase_strip_setup = \ + srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` +am__nobase_strip = \ + for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" +am__nobase_list = $(am__nobase_strip_setup); \ + for p in $$list; do echo "$$p $$p"; done | \ + sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ + $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ + if (++n[$$2] == $(am__install_max)) \ + { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ + END { for (dir in files) print dir, files[dir] }' +am__base_list = \ + sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ + sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' +am__uninstall_files_from_dir = { \ + test -z "$$files" \ + || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ + || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ + $(am__cd) "$$dir" && rm -f $$files; }; \ + } +am__installdirs = "$(DESTDIR)$(libdir)" "$(DESTDIR)$(sbindir)" LTLIBRARIES = $(lib_LTLIBRARIES) am__DEPENDENCIES_1 = @NO_RDYNAMIC_TRUE@liblightcomp_la_DEPENDENCIES = \ @NO_RDYNAMIC_TRUE@ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \ -@NO_RDYNAMIC_TRUE@ $(am__DEPENDENCIES_1) +@NO_RDYNAMIC_TRUE@ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) am__liblightcomp_la_SOURCES_DIST = buffer.c log.c keyvalue.c chunk.c \ http_chunk.c stream.c fdevent.c stat_cache.c plugin.c \ joblist.c etag.c array.c data_string.c data_count.c \ data_array.c data_integer.c md5.c data_fastcgi.c \ - fdevent_select.c fdevent_linux_rtsig.c fdevent_poll.c \ + fdevent_select.c fdevent_libev.c fdevent_poll.c \ fdevent_linux_sysepoll.c fdevent_solaris_devpoll.c \ - fdevent_freebsd_kqueue.c data_config.c bitset.c \ - inet_ntop_cache.c crc32.c connections-glue.c configfile-glue.c \ - http-header-glue.c network_write.c network_linux_sendfile.c \ - network_freebsd_sendfile.c network_writev.c \ - network_solaris_sendfilev.c network_openssl.c splaytree.c + fdevent_solaris_port.c fdevent_freebsd_kqueue.c data_config.c \ + bitset.c inet_ntop_cache.c crc32.c connections-glue.c \ + configfile-glue.c http-header-glue.c network_write.c \ + network_linux_sendfile.c network_freebsd_sendfile.c \ + network_writev.c network_solaris_sendfilev.c network_openssl.c \ + splaytree.c status_counter.c am__objects_1 = liblightcomp_la-buffer.lo liblightcomp_la-log.lo \ liblightcomp_la-keyvalue.lo liblightcomp_la-chunk.lo \ liblightcomp_la-http_chunk.lo liblightcomp_la-stream.lo \ @@ -95,10 +131,11 @@ am__objects_1 = liblightcomp_la-buffer.lo liblightcomp_la-log.lo \ liblightcomp_la-data_array.lo liblightcomp_la-data_integer.lo \ liblightcomp_la-md5.lo liblightcomp_la-data_fastcgi.lo \ liblightcomp_la-fdevent_select.lo \ - liblightcomp_la-fdevent_linux_rtsig.lo \ + liblightcomp_la-fdevent_libev.lo \ liblightcomp_la-fdevent_poll.lo \ liblightcomp_la-fdevent_linux_sysepoll.lo \ liblightcomp_la-fdevent_solaris_devpoll.lo \ + liblightcomp_la-fdevent_solaris_port.lo \ liblightcomp_la-fdevent_freebsd_kqueue.lo \ liblightcomp_la-data_config.lo liblightcomp_la-bitset.lo \ liblightcomp_la-inet_ntop_cache.lo liblightcomp_la-crc32.lo \ @@ -111,113 +148,244 @@ am__objects_1 = liblightcomp_la-buffer.lo liblightcomp_la-log.lo \ liblightcomp_la-network_writev.lo \ liblightcomp_la-network_solaris_sendfilev.lo \ liblightcomp_la-network_openssl.lo \ - liblightcomp_la-splaytree.lo + liblightcomp_la-splaytree.lo liblightcomp_la-status_counter.lo @NO_RDYNAMIC_TRUE@am_liblightcomp_la_OBJECTS = $(am__objects_1) liblightcomp_la_OBJECTS = $(am_liblightcomp_la_OBJECTS) +AM_V_lt = $(am__v_lt_@AM_V@) +am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) +am__v_lt_0 = --silent +liblightcomp_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ + $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ + $(liblightcomp_la_CFLAGS) $(CFLAGS) $(liblightcomp_la_LDFLAGS) \ + $(LDFLAGS) -o $@ @NO_RDYNAMIC_TRUE@am_liblightcomp_la_rpath = -rpath $(libdir) @NO_RDYNAMIC_TRUE@am__DEPENDENCIES_2 = liblightcomp.la mod_access_la_DEPENDENCIES = $(am__DEPENDENCIES_2) am_mod_access_la_OBJECTS = mod_access.lo mod_access_la_OBJECTS = $(am_mod_access_la_OBJECTS) +mod_access_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(mod_access_la_LDFLAGS) $(LDFLAGS) -o $@ mod_accesslog_la_DEPENDENCIES = $(am__DEPENDENCIES_2) am_mod_accesslog_la_OBJECTS = mod_accesslog.lo mod_accesslog_la_OBJECTS = $(am_mod_accesslog_la_OBJECTS) +mod_accesslog_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ + $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ + $(AM_CFLAGS) $(CFLAGS) $(mod_accesslog_la_LDFLAGS) $(LDFLAGS) \ + -o $@ mod_alias_la_DEPENDENCIES = $(am__DEPENDENCIES_2) am_mod_alias_la_OBJECTS = mod_alias.lo mod_alias_la_OBJECTS = $(am_mod_alias_la_OBJECTS) +mod_alias_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(mod_alias_la_LDFLAGS) $(LDFLAGS) -o $@ mod_auth_la_DEPENDENCIES = $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_2) -am_mod_auth_la_OBJECTS = mod_auth.lo http_auth_digest.lo http_auth.lo +am_mod_auth_la_OBJECTS = mod_auth.lo http_auth.lo mod_auth_la_OBJECTS = $(am_mod_auth_la_OBJECTS) +mod_auth_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(mod_auth_la_LDFLAGS) $(LDFLAGS) -o $@ mod_cgi_la_DEPENDENCIES = $(am__DEPENDENCIES_2) am_mod_cgi_la_OBJECTS = mod_cgi.lo mod_cgi_la_OBJECTS = $(am_mod_cgi_la_OBJECTS) +mod_cgi_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(mod_cgi_la_LDFLAGS) $(LDFLAGS) -o $@ mod_cml_la_DEPENDENCIES = $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_2) \ $(am__DEPENDENCIES_1) am_mod_cml_la_OBJECTS = mod_cml_la-mod_cml.lo \ mod_cml_la-mod_cml_lua.lo mod_cml_la-mod_cml_funcs.lo mod_cml_la_OBJECTS = $(am_mod_cml_la_OBJECTS) +mod_cml_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(mod_cml_la_CFLAGS) \ + $(CFLAGS) $(mod_cml_la_LDFLAGS) $(LDFLAGS) -o $@ mod_compress_la_DEPENDENCIES = $(am__DEPENDENCIES_1) \ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_2) am_mod_compress_la_OBJECTS = mod_compress.lo mod_compress_la_OBJECTS = $(am_mod_compress_la_OBJECTS) +mod_compress_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ + $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ + $(AM_CFLAGS) $(CFLAGS) $(mod_compress_la_LDFLAGS) $(LDFLAGS) \ + -o $@ mod_dirlisting_la_DEPENDENCIES = $(am__DEPENDENCIES_2) \ $(am__DEPENDENCIES_1) am_mod_dirlisting_la_OBJECTS = mod_dirlisting.lo mod_dirlisting_la_OBJECTS = $(am_mod_dirlisting_la_OBJECTS) +mod_dirlisting_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ + $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ + $(AM_CFLAGS) $(CFLAGS) $(mod_dirlisting_la_LDFLAGS) $(LDFLAGS) \ + -o $@ mod_evasive_la_DEPENDENCIES = $(am__DEPENDENCIES_2) am_mod_evasive_la_OBJECTS = mod_evasive.lo mod_evasive_la_OBJECTS = $(am_mod_evasive_la_OBJECTS) +mod_evasive_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ + $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ + $(AM_CFLAGS) $(CFLAGS) $(mod_evasive_la_LDFLAGS) $(LDFLAGS) -o \ + $@ mod_evhost_la_DEPENDENCIES = $(am__DEPENDENCIES_2) am_mod_evhost_la_OBJECTS = mod_evhost.lo mod_evhost_la_OBJECTS = $(am_mod_evhost_la_OBJECTS) +mod_evhost_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(mod_evhost_la_LDFLAGS) $(LDFLAGS) -o $@ mod_expire_la_DEPENDENCIES = $(am__DEPENDENCIES_2) am_mod_expire_la_OBJECTS = mod_expire.lo mod_expire_la_OBJECTS = $(am_mod_expire_la_OBJECTS) +mod_expire_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(mod_expire_la_LDFLAGS) $(LDFLAGS) -o $@ +mod_extforward_la_DEPENDENCIES = $(am__DEPENDENCIES_2) +am_mod_extforward_la_OBJECTS = mod_extforward.lo +mod_extforward_la_OBJECTS = $(am_mod_extforward_la_OBJECTS) +mod_extforward_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ + $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ + $(AM_CFLAGS) $(CFLAGS) $(mod_extforward_la_LDFLAGS) $(LDFLAGS) \ + -o $@ mod_fastcgi_la_DEPENDENCIES = $(am__DEPENDENCIES_2) am_mod_fastcgi_la_OBJECTS = mod_fastcgi.lo mod_fastcgi_la_OBJECTS = $(am_mod_fastcgi_la_OBJECTS) +mod_fastcgi_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ + $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ + $(AM_CFLAGS) $(CFLAGS) $(mod_fastcgi_la_LDFLAGS) $(LDFLAGS) -o \ + $@ +mod_flv_streaming_la_DEPENDENCIES = $(am__DEPENDENCIES_2) +am_mod_flv_streaming_la_OBJECTS = mod_flv_streaming.lo +mod_flv_streaming_la_OBJECTS = $(am_mod_flv_streaming_la_OBJECTS) +mod_flv_streaming_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ + $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ + $(AM_CFLAGS) $(CFLAGS) $(mod_flv_streaming_la_LDFLAGS) \ + $(LDFLAGS) -o $@ mod_indexfile_la_DEPENDENCIES = $(am__DEPENDENCIES_2) am_mod_indexfile_la_OBJECTS = mod_indexfile.lo mod_indexfile_la_OBJECTS = $(am_mod_indexfile_la_OBJECTS) +mod_indexfile_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ + $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ + $(AM_CFLAGS) $(CFLAGS) $(mod_indexfile_la_LDFLAGS) $(LDFLAGS) \ + -o $@ +mod_magnet_la_DEPENDENCIES = $(am__DEPENDENCIES_2) \ + $(am__DEPENDENCIES_1) +am_mod_magnet_la_OBJECTS = mod_magnet_la-mod_magnet.lo \ + mod_magnet_la-mod_magnet_cache.lo +mod_magnet_la_OBJECTS = $(am_mod_magnet_la_OBJECTS) +mod_magnet_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(mod_magnet_la_CFLAGS) \ + $(CFLAGS) $(mod_magnet_la_LDFLAGS) $(LDFLAGS) -o $@ mod_mysql_vhost_la_DEPENDENCIES = $(am__DEPENDENCIES_1) \ $(am__DEPENDENCIES_2) am_mod_mysql_vhost_la_OBJECTS = mod_mysql_vhost_la-mod_mysql_vhost.lo mod_mysql_vhost_la_OBJECTS = $(am_mod_mysql_vhost_la_OBJECTS) +mod_mysql_vhost_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ + $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ + $(AM_CFLAGS) $(CFLAGS) $(mod_mysql_vhost_la_LDFLAGS) \ + $(LDFLAGS) -o $@ mod_proxy_la_DEPENDENCIES = $(am__DEPENDENCIES_2) am_mod_proxy_la_OBJECTS = mod_proxy.lo mod_proxy_la_OBJECTS = $(am_mod_proxy_la_OBJECTS) +mod_proxy_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(mod_proxy_la_LDFLAGS) $(LDFLAGS) -o $@ mod_redirect_la_DEPENDENCIES = $(am__DEPENDENCIES_1) \ $(am__DEPENDENCIES_2) am_mod_redirect_la_OBJECTS = mod_redirect.lo mod_redirect_la_OBJECTS = $(am_mod_redirect_la_OBJECTS) +mod_redirect_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ + $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ + $(AM_CFLAGS) $(CFLAGS) $(mod_redirect_la_LDFLAGS) $(LDFLAGS) \ + -o $@ mod_rewrite_la_DEPENDENCIES = $(am__DEPENDENCIES_1) \ $(am__DEPENDENCIES_2) am_mod_rewrite_la_OBJECTS = mod_rewrite.lo mod_rewrite_la_OBJECTS = $(am_mod_rewrite_la_OBJECTS) +mod_rewrite_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ + $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ + $(AM_CFLAGS) $(CFLAGS) $(mod_rewrite_la_LDFLAGS) $(LDFLAGS) -o \ + $@ mod_rrdtool_la_DEPENDENCIES = $(am__DEPENDENCIES_2) am_mod_rrdtool_la_OBJECTS = mod_rrdtool.lo mod_rrdtool_la_OBJECTS = $(am_mod_rrdtool_la_OBJECTS) +mod_rrdtool_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ + $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ + $(AM_CFLAGS) $(CFLAGS) $(mod_rrdtool_la_LDFLAGS) $(LDFLAGS) -o \ + $@ mod_scgi_la_DEPENDENCIES = $(am__DEPENDENCIES_2) am_mod_scgi_la_OBJECTS = mod_scgi.lo mod_scgi_la_OBJECTS = $(am_mod_scgi_la_OBJECTS) +mod_scgi_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(mod_scgi_la_LDFLAGS) $(LDFLAGS) -o $@ mod_secdownload_la_DEPENDENCIES = $(am__DEPENDENCIES_2) am_mod_secdownload_la_OBJECTS = mod_secure_download.lo mod_secdownload_la_OBJECTS = $(am_mod_secdownload_la_OBJECTS) +mod_secdownload_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ + $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ + $(AM_CFLAGS) $(CFLAGS) $(mod_secdownload_la_LDFLAGS) \ + $(LDFLAGS) -o $@ mod_setenv_la_DEPENDENCIES = $(am__DEPENDENCIES_2) am_mod_setenv_la_OBJECTS = mod_setenv.lo mod_setenv_la_OBJECTS = $(am_mod_setenv_la_OBJECTS) +mod_setenv_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(mod_setenv_la_LDFLAGS) $(LDFLAGS) -o $@ mod_simple_vhost_la_DEPENDENCIES = $(am__DEPENDENCIES_2) am_mod_simple_vhost_la_OBJECTS = mod_simple_vhost.lo mod_simple_vhost_la_OBJECTS = $(am_mod_simple_vhost_la_OBJECTS) +mod_simple_vhost_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ + $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ + $(AM_CFLAGS) $(CFLAGS) $(mod_simple_vhost_la_LDFLAGS) \ + $(LDFLAGS) -o $@ mod_ssi_la_DEPENDENCIES = $(am__DEPENDENCIES_2) $(am__DEPENDENCIES_1) am_mod_ssi_la_OBJECTS = mod_ssi_exprparser.lo mod_ssi_expr.lo \ mod_ssi.lo mod_ssi_la_OBJECTS = $(am_mod_ssi_la_OBJECTS) +mod_ssi_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(mod_ssi_la_LDFLAGS) $(LDFLAGS) -o $@ mod_staticfile_la_DEPENDENCIES = $(am__DEPENDENCIES_2) am_mod_staticfile_la_OBJECTS = mod_staticfile.lo mod_staticfile_la_OBJECTS = $(am_mod_staticfile_la_OBJECTS) +mod_staticfile_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ + $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ + $(AM_CFLAGS) $(CFLAGS) $(mod_staticfile_la_LDFLAGS) $(LDFLAGS) \ + -o $@ mod_status_la_DEPENDENCIES = $(am__DEPENDENCIES_2) am_mod_status_la_OBJECTS = mod_status.lo mod_status_la_OBJECTS = $(am_mod_status_la_OBJECTS) +mod_status_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(mod_status_la_LDFLAGS) $(LDFLAGS) -o $@ mod_trigger_b4_dl_la_DEPENDENCIES = $(am__DEPENDENCIES_1) \ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \ $(am__DEPENDENCIES_2) am_mod_trigger_b4_dl_la_OBJECTS = mod_trigger_b4_dl.lo mod_trigger_b4_dl_la_OBJECTS = $(am_mod_trigger_b4_dl_la_OBJECTS) +mod_trigger_b4_dl_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ + $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ + $(AM_CFLAGS) $(CFLAGS) $(mod_trigger_b4_dl_la_LDFLAGS) \ + $(LDFLAGS) -o $@ mod_userdir_la_DEPENDENCIES = $(am__DEPENDENCIES_2) am_mod_userdir_la_OBJECTS = mod_userdir.lo mod_userdir_la_OBJECTS = $(am_mod_userdir_la_OBJECTS) +mod_userdir_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ + $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ + $(AM_CFLAGS) $(CFLAGS) $(mod_userdir_la_LDFLAGS) $(LDFLAGS) -o \ + $@ mod_usertrack_la_DEPENDENCIES = $(am__DEPENDENCIES_2) am_mod_usertrack_la_OBJECTS = mod_usertrack.lo mod_usertrack_la_OBJECTS = $(am_mod_usertrack_la_OBJECTS) +mod_usertrack_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ + $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ + $(AM_CFLAGS) $(CFLAGS) $(mod_usertrack_la_LDFLAGS) $(LDFLAGS) \ + -o $@ mod_webdav_la_DEPENDENCIES = $(am__DEPENDENCIES_2) \ - $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) + $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \ + $(am__DEPENDENCIES_1) am_mod_webdav_la_OBJECTS = mod_webdav_la-mod_webdav.lo mod_webdav_la_OBJECTS = $(am_mod_webdav_la_OBJECTS) -binPROGRAMS_INSTALL = $(INSTALL_PROGRAM) -sbinPROGRAMS_INSTALL = $(INSTALL_PROGRAM) -PROGRAMS = $(bin_PROGRAMS) $(noinst_PROGRAMS) $(sbin_PROGRAMS) +mod_webdav_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(mod_webdav_la_CFLAGS) \ + $(CFLAGS) $(mod_webdav_la_LDFLAGS) $(LDFLAGS) -o $@ +PROGRAMS = $(noinst_PROGRAMS) $(sbin_PROGRAMS) am_lemon_OBJECTS = lemon.$(OBJEXT) lemon_OBJECTS = $(am_lemon_OBJECTS) lemon_LDADD = $(LDADD) @@ -226,14 +394,14 @@ am__lighttpd_SOURCES_DIST = server.c response.c connections.c \ buffer.c log.c keyvalue.c chunk.c http_chunk.c stream.c \ fdevent.c stat_cache.c plugin.c joblist.c etag.c array.c \ data_string.c data_count.c data_array.c data_integer.c md5.c \ - data_fastcgi.c fdevent_select.c fdevent_linux_rtsig.c \ - fdevent_poll.c fdevent_linux_sysepoll.c \ - fdevent_solaris_devpoll.c fdevent_freebsd_kqueue.c \ - data_config.c bitset.c inet_ntop_cache.c crc32.c \ - connections-glue.c configfile-glue.c http-header-glue.c \ - network_write.c network_linux_sendfile.c \ - network_freebsd_sendfile.c network_writev.c \ - network_solaris_sendfilev.c network_openssl.c splaytree.c + data_fastcgi.c fdevent_select.c fdevent_libev.c fdevent_poll.c \ + fdevent_linux_sysepoll.c fdevent_solaris_devpoll.c \ + fdevent_solaris_port.c fdevent_freebsd_kqueue.c data_config.c \ + bitset.c inet_ntop_cache.c crc32.c connections-glue.c \ + configfile-glue.c http-header-glue.c network_write.c \ + network_linux_sendfile.c network_freebsd_sendfile.c \ + network_writev.c network_solaris_sendfilev.c network_openssl.c \ + splaytree.c status_counter.c am__objects_2 = buffer.$(OBJEXT) log.$(OBJEXT) keyvalue.$(OBJEXT) \ chunk.$(OBJEXT) http_chunk.$(OBJEXT) stream.$(OBJEXT) \ fdevent.$(OBJEXT) stat_cache.$(OBJEXT) plugin.$(OBJEXT) \ @@ -241,9 +409,10 @@ am__objects_2 = buffer.$(OBJEXT) log.$(OBJEXT) keyvalue.$(OBJEXT) \ data_string.$(OBJEXT) data_count.$(OBJEXT) \ data_array.$(OBJEXT) data_integer.$(OBJEXT) md5.$(OBJEXT) \ data_fastcgi.$(OBJEXT) fdevent_select.$(OBJEXT) \ - fdevent_linux_rtsig.$(OBJEXT) fdevent_poll.$(OBJEXT) \ + fdevent_libev.$(OBJEXT) fdevent_poll.$(OBJEXT) \ fdevent_linux_sysepoll.$(OBJEXT) \ fdevent_solaris_devpoll.$(OBJEXT) \ + fdevent_solaris_port.$(OBJEXT) \ fdevent_freebsd_kqueue.$(OBJEXT) data_config.$(OBJEXT) \ bitset.$(OBJEXT) inet_ntop_cache.$(OBJEXT) crc32.$(OBJEXT) \ connections-glue.$(OBJEXT) configfile-glue.$(OBJEXT) \ @@ -251,7 +420,7 @@ am__objects_2 = buffer.$(OBJEXT) log.$(OBJEXT) keyvalue.$(OBJEXT) \ network_linux_sendfile.$(OBJEXT) \ network_freebsd_sendfile.$(OBJEXT) network_writev.$(OBJEXT) \ network_solaris_sendfilev.$(OBJEXT) network_openssl.$(OBJEXT) \ - splaytree.$(OBJEXT) + splaytree.$(OBJEXT) status_counter.$(OBJEXT) @NO_RDYNAMIC_FALSE@am__objects_3 = $(am__objects_2) am__objects_4 = server.$(OBJEXT) response.$(OBJEXT) \ connections.$(OBJEXT) network.$(OBJEXT) configfile.$(OBJEXT) \ @@ -262,32 +431,70 @@ lighttpd_OBJECTS = $(am_lighttpd_OBJECTS) lighttpd_DEPENDENCIES = $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \ $(am__DEPENDENCIES_2) $(am__DEPENDENCIES_1) \ - $(am__DEPENDENCIES_1) + $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) +lighttpd_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(lighttpd_LDFLAGS) $(LDFLAGS) -o $@ +am_lighttpd_angel_OBJECTS = lighttpd-angel.$(OBJEXT) +lighttpd_angel_OBJECTS = $(am_lighttpd_angel_OBJECTS) +lighttpd_angel_LDADD = $(LDADD) am_proc_open_OBJECTS = proc_open-proc_open.$(OBJEXT) \ proc_open-buffer.$(OBJEXT) proc_open_OBJECTS = $(am_proc_open_OBJECTS) proc_open_LDADD = $(LDADD) -am_spawn_fcgi_OBJECTS = spawn-fcgi.$(OBJEXT) -spawn_fcgi_OBJECTS = $(am_spawn_fcgi_OBJECTS) -spawn_fcgi_LDADD = $(LDADD) -DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir) +DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = $(SHELL) $(top_srcdir)/depcomp am__depfiles_maybe = depfiles +am__mv = mv -f COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -LTCOMPILE = $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) \ +LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CFLAGS) $(CFLAGS) +AM_V_CC = $(am__v_CC_@AM_V@) +am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) +am__v_CC_0 = @echo " CC " $@; +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ CCLD = $(CC) -LINK = $(LIBTOOL) --tag=CC --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ +LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_CCLD = $(am__v_CCLD_@AM_V@) +am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) +am__v_CCLD_0 = @echo " CCLD " $@; +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; SOURCES = $(liblightcomp_la_SOURCES) $(mod_access_la_SOURCES) \ $(mod_accesslog_la_SOURCES) $(mod_alias_la_SOURCES) \ $(mod_auth_la_SOURCES) $(mod_cgi_la_SOURCES) \ $(mod_cml_la_SOURCES) $(mod_compress_la_SOURCES) \ $(mod_dirlisting_la_SOURCES) $(mod_evasive_la_SOURCES) \ $(mod_evhost_la_SOURCES) $(mod_expire_la_SOURCES) \ - $(mod_fastcgi_la_SOURCES) $(mod_indexfile_la_SOURCES) \ + $(mod_extforward_la_SOURCES) $(mod_fastcgi_la_SOURCES) \ + $(mod_flv_streaming_la_SOURCES) $(mod_indexfile_la_SOURCES) \ + $(mod_magnet_la_SOURCES) $(mod_mysql_vhost_la_SOURCES) \ + $(mod_proxy_la_SOURCES) $(mod_redirect_la_SOURCES) \ + $(mod_rewrite_la_SOURCES) $(mod_rrdtool_la_SOURCES) \ + $(mod_scgi_la_SOURCES) $(mod_secdownload_la_SOURCES) \ + $(mod_setenv_la_SOURCES) $(mod_simple_vhost_la_SOURCES) \ + $(mod_ssi_la_SOURCES) $(mod_staticfile_la_SOURCES) \ + $(mod_status_la_SOURCES) $(mod_trigger_b4_dl_la_SOURCES) \ + $(mod_userdir_la_SOURCES) $(mod_usertrack_la_SOURCES) \ + $(mod_webdav_la_SOURCES) $(lemon_SOURCES) $(lighttpd_SOURCES) \ + $(lighttpd_angel_SOURCES) $(proc_open_SOURCES) +DIST_SOURCES = $(am__liblightcomp_la_SOURCES_DIST) \ + $(mod_access_la_SOURCES) $(mod_accesslog_la_SOURCES) \ + $(mod_alias_la_SOURCES) $(mod_auth_la_SOURCES) \ + $(mod_cgi_la_SOURCES) $(mod_cml_la_SOURCES) \ + $(mod_compress_la_SOURCES) $(mod_dirlisting_la_SOURCES) \ + $(mod_evasive_la_SOURCES) $(mod_evhost_la_SOURCES) \ + $(mod_expire_la_SOURCES) $(mod_extforward_la_SOURCES) \ + $(mod_fastcgi_la_SOURCES) $(mod_flv_streaming_la_SOURCES) \ + $(mod_indexfile_la_SOURCES) $(mod_magnet_la_SOURCES) \ $(mod_mysql_vhost_la_SOURCES) $(mod_proxy_la_SOURCES) \ $(mod_redirect_la_SOURCES) $(mod_rewrite_la_SOURCES) \ $(mod_rrdtool_la_SOURCES) $(mod_scgi_la_SOURCES) \ @@ -296,34 +503,20 @@ SOURCES = $(liblightcomp_la_SOURCES) $(mod_access_la_SOURCES) \ $(mod_staticfile_la_SOURCES) $(mod_status_la_SOURCES) \ $(mod_trigger_b4_dl_la_SOURCES) $(mod_userdir_la_SOURCES) \ $(mod_usertrack_la_SOURCES) $(mod_webdav_la_SOURCES) \ - $(lemon_SOURCES) $(lighttpd_SOURCES) $(proc_open_SOURCES) \ - $(spawn_fcgi_SOURCES) -DIST_SOURCES = $(am__liblightcomp_la_SOURCES_DIST) \ - $(mod_access_la_SOURCES) $(mod_accesslog_la_SOURCES) \ - $(mod_alias_la_SOURCES) $(mod_auth_la_SOURCES) \ - $(mod_cgi_la_SOURCES) $(mod_cml_la_SOURCES) \ - $(mod_compress_la_SOURCES) $(mod_dirlisting_la_SOURCES) \ - $(mod_evasive_la_SOURCES) $(mod_evhost_la_SOURCES) \ - $(mod_expire_la_SOURCES) $(mod_fastcgi_la_SOURCES) \ - $(mod_indexfile_la_SOURCES) $(mod_mysql_vhost_la_SOURCES) \ - $(mod_proxy_la_SOURCES) $(mod_redirect_la_SOURCES) \ - $(mod_rewrite_la_SOURCES) $(mod_rrdtool_la_SOURCES) \ - $(mod_scgi_la_SOURCES) $(mod_secdownload_la_SOURCES) \ - $(mod_setenv_la_SOURCES) $(mod_simple_vhost_la_SOURCES) \ - $(mod_ssi_la_SOURCES) $(mod_staticfile_la_SOURCES) \ - $(mod_status_la_SOURCES) $(mod_trigger_b4_dl_la_SOURCES) \ - $(mod_userdir_la_SOURCES) $(mod_usertrack_la_SOURCES) \ - $(mod_webdav_la_SOURCES) $(lemon_SOURCES) \ - $(am__lighttpd_SOURCES_DIST) $(proc_open_SOURCES) \ - $(spawn_fcgi_SOURCES) + $(lemon_SOURCES) $(am__lighttpd_SOURCES_DIST) \ + $(lighttpd_angel_SOURCES) $(proc_open_SOURCES) +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac HEADERS = $(noinst_HEADERS) ETAGS = etags CTAGS = ctags DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ -AMDEP_FALSE = @AMDEP_FALSE@ -AMDEP_TRUE = @AMDEP_TRUE@ AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AR = @AR@ ATTR_LIB = @ATTR_LIB@ AUTOCONF = @AUTOCONF@ @@ -334,69 +527,73 @@ BZ_LIB = @BZ_LIB@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ -CHECK_WITH_FASTCGI_FALSE = @CHECK_WITH_FASTCGI_FALSE@ -CHECK_WITH_FASTCGI_TRUE = @CHECK_WITH_FASTCGI_TRUE@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ -CROSS_COMPILING_FALSE = @CROSS_COMPILING_FALSE@ -CROSS_COMPILING_TRUE = @CROSS_COMPILING_TRUE@ CRYPT_LIB = @CRYPT_LIB@ -CXX = @CXX@ -CXXCPP = @CXXCPP@ -CXXDEPMODE = @CXXDEPMODE@ -CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ -DEFS = @DEFS@ -DLIBRARY_DIR="\"$(libdir)\"" +DEFS = @DEFS@ -DHAVE_VERSION_H -DLIBRARY_DIR="\"$(libdir)\"" -DSBIN_DIR="\"$(sbindir)\"" DEPDIR = @DEPDIR@ +DLLTOOL = @DLLTOOL@ DL_LIB = @DL_LIB@ -ECHO = @ECHO@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ -F77 = @F77@ FAM_CFLAGS = @FAM_CFLAGS@ FAM_LIBS = @FAM_LIBS@ -FFLAGS = @FFLAGS@ +FGREP = @FGREP@ GDBM_LIB = @GDBM_LIB@ +GREP = @GREP@ +INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ LBER_LIB = @LBER_LIB@ +LD = @LD@ LDAP_LIB = @LDAP_LIB@ LDFLAGS = @LDFLAGS@ +LIBEV_CFLAGS = @LIBEV_CFLAGS@ +LIBEV_LIBS = @LIBEV_LIBS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ LN_S = @LN_S@ LTLIBOBJS = @LTLIBOBJS@ -LUACONFIG = @LUACONFIG@ LUA_CFLAGS = @LUA_CFLAGS@ LUA_LIBS = @LUA_LIBS@ -MAINT = @MAINT@ -MAINTAINER_MODE_FALSE = @MAINTAINER_MODE_FALSE@ -MAINTAINER_MODE_TRUE = @MAINTAINER_MODE_TRUE@ MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ MEMCACHE_LIB = @MEMCACHE_LIB@ +MKDIR_P = @MKDIR_P@ MYSQL_CONFIG = @MYSQL_CONFIG@ MYSQL_INCLUDE = @MYSQL_INCLUDE@ MYSQL_LIBS = @MYSQL_LIBS@ -NO_RDYNAMIC_FALSE = @NO_RDYNAMIC_FALSE@ -NO_RDYNAMIC_TRUE = @NO_RDYNAMIC_TRUE@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PCRECONFIG = @PCRECONFIG@ PCRE_LIB = @PCRE_LIB@ PKG_CONFIG = @PKG_CONFIG@ +PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ +PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ RANLIB = @RANLIB@ +SED = @SED@ SENDFILE_LIB = @SENDFILE_LIB@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ @@ -404,21 +601,18 @@ SQLITE_CFLAGS = @SQLITE_CFLAGS@ SQLITE_LIBS = @SQLITE_LIBS@ SSL_LIB = @SSL_LIB@ STRIP = @STRIP@ -U = @U@ +UUID_LIBS = @UUID_LIBS@ VERSION = @VERSION@ XML_CFLAGS = @XML_CFLAGS@ XML_LIBS = @XML_LIBS@ Z_LIB = @Z_LIB@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ -ac_ct_CXX = @ac_ct_CXX@ -ac_ct_F77 = @ac_ct_F77@ -ac_ct_RANLIB = @ac_ct_RANLIB@ -ac_ct_STRIP = @ac_ct_STRIP@ -am__fastdepCC_FALSE = @am__fastdepCC_FALSE@ -am__fastdepCC_TRUE = @am__fastdepCC_TRUE@ -am__fastdepCXX_FALSE = @am__fastdepCXX_FALSE@ -am__fastdepCXX_TRUE = @am__fastdepCXX_TRUE@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ @@ -430,43 +624,61 @@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ +builddir = @builddir@ datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ +htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ +localedir = @localedir@ localstatedir = @localstatedir@ mandir = @mandir@ mkdir_p = @mkdir_p@ oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ +psdir = @psdir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ sysconfdir = @sysconfdir@ target = @target@ target_alias = @target_alias@ target_cpu = @target_cpu@ target_os = @target_os@ target_vendor = @target_vendor@ -LEMON = $(top_builddir)/src/lemon +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +AM_CFLAGS = $(FAM_CFLAGS) +LEMON = $(top_builddir)/src/lemon$(EXEEXT) lemon_SOURCES = lemon.c +lighttpd_angel_SOURCES = lighttpd-angel.c +BUILT_SOURCES = parsers versionstamp +MAINTAINERCLEANFILES = configparser.c configparser.h mod_ssi_exprparser.c mod_ssi_exprparser.h +CLEANFILES = versionstamp.h versionstamp.h.tmp common_src = buffer.c log.c \ keyvalue.c chunk.c \ http_chunk.c stream.c fdevent.c \ stat_cache.c plugin.c joblist.c etag.c array.c \ data_string.c data_count.c data_array.c \ data_integer.c md5.c data_fastcgi.c \ - fdevent_select.c fdevent_linux_rtsig.c \ + fdevent_select.c fdevent_libev.c \ fdevent_poll.c fdevent_linux_sysepoll.c \ - fdevent_solaris_devpoll.c fdevent_freebsd_kqueue.c \ + fdevent_solaris_devpoll.c fdevent_solaris_port.c \ + fdevent_freebsd_kqueue.c \ data_config.c bitset.c \ inet_ntop_cache.c crc32.c \ connections-glue.c \ @@ -475,38 +687,44 @@ common_src = buffer.c log.c \ network_write.c network_linux_sendfile.c \ network_freebsd_sendfile.c network_writev.c \ network_solaris_sendfilev.c network_openssl.c \ - splaytree.c + splaytree.c status_counter.c src = server.c response.c connections.c network.c configfile.c \ configparser.c request.c proc_open.c $(am__append_2) -spawn_fcgi_SOURCES = spawn-fcgi.c #lib_LTLIBRARIES += mod_httptls.la #mod_httptls_la_SOURCES = mod_httptls.c #mod_httptls_la_LDFLAGS = -module -export-dynamic -avoid-version -no-undefined #mod_httptls_la_LIBADD = $(common_libadd) -lib_LTLIBRARIES = $(am__append_1) mod_evasive.la mod_webdav.la \ - mod_cml.la mod_trigger_b4_dl.la mod_mysql_vhost.la mod_cgi.la \ - mod_scgi.la mod_staticfile.la mod_dirlisting.la \ - mod_indexfile.la mod_setenv.la mod_alias.la mod_userdir.la \ - mod_rrdtool.la mod_usertrack.la mod_proxy.la mod_ssi.la \ - mod_secdownload.la mod_expire.la mod_evhost.la \ - mod_simple_vhost.la mod_fastcgi.la mod_access.la \ - mod_compress.la mod_auth.la mod_rewrite.la mod_redirect.la \ - mod_status.la mod_accesslog.la +lib_LTLIBRARIES = $(am__append_1) mod_flv_streaming.la mod_evasive.la \ + mod_webdav.la mod_magnet.la mod_cml.la mod_trigger_b4_dl.la \ + mod_mysql_vhost.la mod_cgi.la mod_scgi.la mod_staticfile.la \ + mod_dirlisting.la mod_indexfile.la mod_setenv.la mod_alias.la \ + mod_userdir.la mod_rrdtool.la mod_usertrack.la mod_proxy.la \ + mod_ssi.la mod_secdownload.la mod_expire.la mod_evhost.la \ + mod_simple_vhost.la mod_fastcgi.la mod_extforward.la \ + mod_access.la mod_compress.la mod_auth.la mod_rewrite.la \ + mod_redirect.la mod_status.la mod_accesslog.la @NO_RDYNAMIC_TRUE@liblightcomp_la_SOURCES = $(common_src) -@NO_RDYNAMIC_TRUE@liblightcomp_la_CFLAGS = $(AM_CFLAGS) $(FAM_CFLAGS) +@NO_RDYNAMIC_TRUE@liblightcomp_la_CFLAGS = $(AM_CFLAGS) $(LIBEV_CFLAGS) @NO_RDYNAMIC_TRUE@liblightcomp_la_LDFLAGS = -avoid-version -no-undefined -@NO_RDYNAMIC_TRUE@liblightcomp_la_LIBADD = $(PCRE_LIB) $(SSL_LIB) $(FAM_LIBS) +@NO_RDYNAMIC_TRUE@liblightcomp_la_LIBADD = $(PCRE_LIB) $(SSL_LIB) $(FAM_LIBS) $(LIBEV_LIBS) @NO_RDYNAMIC_FALSE@common_libadd = @NO_RDYNAMIC_TRUE@common_libadd = liblightcomp.la +mod_flv_streaming_la_SOURCES = mod_flv_streaming.c +mod_flv_streaming_la_LDFLAGS = -module -export-dynamic -avoid-version -no-undefined +mod_flv_streaming_la_LIBADD = $(common_libadd) mod_evasive_la_SOURCES = mod_evasive.c mod_evasive_la_LDFLAGS = -module -export-dynamic -avoid-version -no-undefined mod_evasive_la_LIBADD = $(common_libadd) mod_webdav_la_SOURCES = mod_webdav.c -mod_webdav_la_CFLAGS = $(XML_CFLAGS) +mod_webdav_la_CFLAGS = $(AM_CFLAGS) $(XML_CFLAGS) $(SQLITE_CFLAGS) mod_webdav_la_LDFLAGS = -module -export-dynamic -avoid-version -no-undefined -mod_webdav_la_LIBADD = $(common_libadd) $(XML_LIBS) $(SQLITE_LIBS) +mod_webdav_la_LIBADD = $(common_libadd) $(XML_LIBS) $(SQLITE_LIBS) $(UUID_LIBS) +mod_magnet_la_SOURCES = mod_magnet.c mod_magnet_cache.c +mod_magnet_la_CFLAGS = $(AM_CFLAGS) $(LUA_CFLAGS) +mod_magnet_la_LDFLAGS = -module -export-dynamic -avoid-version -no-undefined +mod_magnet_la_LIBADD = $(common_libadd) $(LUA_LIBS) -lm mod_cml_la_SOURCES = mod_cml.c mod_cml_lua.c mod_cml_funcs.c mod_cml_la_CFLAGS = $(AM_CFLAGS) $(LUA_CFLAGS) mod_cml_la_LDFLAGS = -module -export-dynamic -avoid-version -no-undefined @@ -518,28 +736,28 @@ mod_mysql_vhost_la_SOURCES = mod_mysql_vhost.c mod_mysql_vhost_la_LDFLAGS = -module -export-dynamic -avoid-version -no-undefined mod_mysql_vhost_la_LIBADD = $(MYSQL_LIBS) $(common_libadd) mod_mysql_vhost_la_CPPFLAGS = $(MYSQL_INCLUDE) -mod_cgi_la_SOURCES = mod_cgi.c +mod_cgi_la_SOURCES = mod_cgi.c mod_cgi_la_LDFLAGS = -module -export-dynamic -avoid-version -no-undefined mod_cgi_la_LIBADD = $(common_libadd) -mod_scgi_la_SOURCES = mod_scgi.c +mod_scgi_la_SOURCES = mod_scgi.c mod_scgi_la_LDFLAGS = -module -export-dynamic -avoid-version -no-undefined mod_scgi_la_LIBADD = $(common_libadd) -mod_staticfile_la_SOURCES = mod_staticfile.c +mod_staticfile_la_SOURCES = mod_staticfile.c mod_staticfile_la_LDFLAGS = -module -export-dynamic -avoid-version -no-undefined mod_staticfile_la_LIBADD = $(common_libadd) -mod_dirlisting_la_SOURCES = mod_dirlisting.c +mod_dirlisting_la_SOURCES = mod_dirlisting.c mod_dirlisting_la_LDFLAGS = -module -export-dynamic -avoid-version -no-undefined mod_dirlisting_la_LIBADD = $(common_libadd) $(PCRE_LIB) -mod_indexfile_la_SOURCES = mod_indexfile.c +mod_indexfile_la_SOURCES = mod_indexfile.c mod_indexfile_la_LDFLAGS = -module -export-dynamic -avoid-version -no-undefined mod_indexfile_la_LIBADD = $(common_libadd) -mod_setenv_la_SOURCES = mod_setenv.c +mod_setenv_la_SOURCES = mod_setenv.c mod_setenv_la_LDFLAGS = -module -export-dynamic -avoid-version -no-undefined mod_setenv_la_LIBADD = $(common_libadd) -mod_alias_la_SOURCES = mod_alias.c +mod_alias_la_SOURCES = mod_alias.c mod_alias_la_LDFLAGS = -module -export-dynamic -avoid-version -no-undefined mod_alias_la_LIBADD = $(common_libadd) -mod_userdir_la_SOURCES = mod_userdir.c +mod_userdir_la_SOURCES = mod_userdir.c mod_userdir_la_LDFLAGS = -module -export-dynamic -avoid-version -no-undefined mod_userdir_la_LIBADD = $(common_libadd) mod_rrdtool_la_SOURCES = mod_rrdtool.c @@ -551,7 +769,7 @@ mod_usertrack_la_LIBADD = $(common_libadd) mod_proxy_la_SOURCES = mod_proxy.c mod_proxy_la_LDFLAGS = -module -export-dynamic -avoid-version -no-undefined mod_proxy_la_LIBADD = $(common_libadd) -mod_ssi_la_SOURCES = mod_ssi_exprparser.c mod_ssi_expr.c mod_ssi.c +mod_ssi_la_SOURCES = mod_ssi_exprparser.c mod_ssi_expr.c mod_ssi.c mod_ssi_la_LDFLAGS = -module -export-dynamic -avoid-version -no-undefined mod_ssi_la_LIBADD = $(common_libadd) $(PCRE_LIB) mod_secdownload_la_SOURCES = mod_secure_download.c @@ -569,13 +787,16 @@ mod_simple_vhost_la_LIBADD = $(common_libadd) mod_fastcgi_la_SOURCES = mod_fastcgi.c mod_fastcgi_la_LDFLAGS = -module -export-dynamic -avoid-version -no-undefined mod_fastcgi_la_LIBADD = $(common_libadd) +mod_extforward_la_SOURCES = mod_extforward.c +mod_extforward_la_LDFLAGS = -module -export-dynamic -avoid-version -no-undefined +mod_extforward_la_LIBADD = $(common_libadd) mod_access_la_SOURCES = mod_access.c mod_access_la_LDFLAGS = -module -export-dynamic -avoid-version -no-undefined mod_access_la_LIBADD = $(common_libadd) -mod_compress_la_SOURCES = mod_compress.c +mod_compress_la_SOURCES = mod_compress.c mod_compress_la_LDFLAGS = -module -export-dynamic -avoid-version -no-undefined mod_compress_la_LIBADD = $(Z_LIB) $(BZ_LIB) $(common_libadd) -mod_auth_la_SOURCES = mod_auth.c http_auth_digest.c http_auth.c +mod_auth_la_SOURCES = mod_auth.c http_auth.c mod_auth_la_LDFLAGS = -module -export-dynamic -avoid-version -no-undefined mod_auth_la_LIBADD = $(CRYPT_LIB) $(LDAP_LIB) $(LBER_LIB) $(common_libadd) mod_rewrite_la_SOURCES = mod_rewrite.c @@ -592,7 +813,7 @@ mod_accesslog_la_LDFLAGS = -module -export-dynamic -avoid-version -no-undefined mod_accesslog_la_LIBADD = $(common_libadd) hdr = server.h buffer.h network.h log.h keyvalue.h \ response.h request.h fastcgi.h chunk.h \ - settings.h http_chunk.h http_auth_digest.h \ + settings.h http_chunk.h \ md5.h http_auth.h stream.h \ fdevent.h connections.h base.h stat_cache.h \ plugin.h mod_auth.h \ @@ -601,12 +822,14 @@ hdr = server.h buffer.h network.h log.h keyvalue.h \ mod_ssi.h mod_ssi_expr.h inet_ntop_cache.h \ configparser.h mod_ssi_exprparser.h \ sys-mmap.h sys-socket.h mod_cml.h mod_cml_funcs.h \ - splaytree.h proc_open.h + splaytree.h proc_open.h status_counter.h \ + mod_magnet_cache.h \ + version.h lighttpd_SOURCES = $(src) -lighttpd_LDADD = $(PCRE_LIB) $(DL_LIB) $(SENDFILE_LIB) $(ATTR_LIB) $(common_libadd) $(SSL_LIB) $(FAM_LIBS) +lighttpd_LDADD = $(PCRE_LIB) $(DL_LIB) $(SENDFILE_LIB) $(ATTR_LIB) $(common_libadd) $(SSL_LIB) $(FAM_LIBS) $(LIBEV_LIBS) lighttpd_LDFLAGS = -export-dynamic -lighttpd_CCPFLAGS = $(FAM_CFLAGS) +lighttpd_CCPFLAGS = $(FAM_CFLAGS) $(LIBEV_CFLAGS) proc_open_SOURCES = proc_open.c buffer.c proc_open_CPPFLAGS = -DDEBUG_PROC_OPEN @@ -623,23 +846,24 @@ proc_open_CPPFLAGS = -DDEBUG_PROC_OPEN #bench_SOURCES = buffer.c bench.c #ajp_SOURCES = ajp.c noinst_HEADERS = $(hdr) -EXTRA_DIST = mod_skeleton.c configparser.y mod_ssi_exprparser.y lempar.c -all: all-am +EXTRA_DIST = mod_skeleton.c configparser.y mod_ssi_exprparser.y lempar.c SConscript +all: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) all-am .SUFFIXES: .SUFFIXES: .c .lo .o .obj -$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ - cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh \ - && exit 0; \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ - echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/Makefile'; \ - cd $(top_srcdir) && \ - $(AUTOMAKE) --gnu src/Makefile + echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign src/Makefile .PRECIOUS: Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ @@ -653,27 +877,33 @@ Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh -$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) +$(top_srcdir)/configure: $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh -$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) +$(ACLOCAL_M4): $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): install-libLTLIBRARIES: $(lib_LTLIBRARIES) @$(NORMAL_INSTALL) - test -z "$(libdir)" || $(mkdir_p) "$(DESTDIR)$(libdir)" - @list='$(lib_LTLIBRARIES)'; for p in $$list; do \ + @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \ + list2=; for p in $$list; do \ if test -f $$p; then \ - f=$(am__strip_dir) \ - echo " $(LIBTOOL) --mode=install $(libLTLIBRARIES_INSTALL) $(INSTALL_STRIP_FLAG) '$$p' '$(DESTDIR)$(libdir)/$$f'"; \ - $(LIBTOOL) --mode=install $(libLTLIBRARIES_INSTALL) $(INSTALL_STRIP_FLAG) "$$p" "$(DESTDIR)$(libdir)/$$f"; \ + list2="$$list2 $$p"; \ else :; fi; \ - done + done; \ + test -z "$$list2" || { \ + echo " $(MKDIR_P) '$(DESTDIR)$(libdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(libdir)" || exit 1; \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(libdir)'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(libdir)"; \ + } uninstall-libLTLIBRARIES: @$(NORMAL_UNINSTALL) - @set -x; list='$(lib_LTLIBRARIES)'; for p in $$list; do \ - p=$(am__strip_dir) \ - echo " $(LIBTOOL) --mode=uninstall rm -f '$(DESTDIR)$(libdir)/$$p'"; \ - $(LIBTOOL) --mode=uninstall rm -f "$(DESTDIR)$(libdir)/$$p"; \ + @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \ + for p in $$list; do \ + $(am__strip_dir) \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(libdir)/$$f'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(libdir)/$$f"; \ done clean-libLTLIBRARIES: @@ -684,141 +914,139 @@ clean-libLTLIBRARIES: echo "rm -f \"$${dir}/so_locations\""; \ rm -f "$${dir}/so_locations"; \ done -liblightcomp.la: $(liblightcomp_la_OBJECTS) $(liblightcomp_la_DEPENDENCIES) - $(LINK) $(am_liblightcomp_la_rpath) $(liblightcomp_la_LDFLAGS) $(liblightcomp_la_OBJECTS) $(liblightcomp_la_LIBADD) $(LIBS) -mod_access.la: $(mod_access_la_OBJECTS) $(mod_access_la_DEPENDENCIES) - $(LINK) -rpath $(libdir) $(mod_access_la_LDFLAGS) $(mod_access_la_OBJECTS) $(mod_access_la_LIBADD) $(LIBS) -mod_accesslog.la: $(mod_accesslog_la_OBJECTS) $(mod_accesslog_la_DEPENDENCIES) - $(LINK) -rpath $(libdir) $(mod_accesslog_la_LDFLAGS) $(mod_accesslog_la_OBJECTS) $(mod_accesslog_la_LIBADD) $(LIBS) -mod_alias.la: $(mod_alias_la_OBJECTS) $(mod_alias_la_DEPENDENCIES) - $(LINK) -rpath $(libdir) $(mod_alias_la_LDFLAGS) $(mod_alias_la_OBJECTS) $(mod_alias_la_LIBADD) $(LIBS) -mod_auth.la: $(mod_auth_la_OBJECTS) $(mod_auth_la_DEPENDENCIES) - $(LINK) -rpath $(libdir) $(mod_auth_la_LDFLAGS) $(mod_auth_la_OBJECTS) $(mod_auth_la_LIBADD) $(LIBS) -mod_cgi.la: $(mod_cgi_la_OBJECTS) $(mod_cgi_la_DEPENDENCIES) - $(LINK) -rpath $(libdir) $(mod_cgi_la_LDFLAGS) $(mod_cgi_la_OBJECTS) $(mod_cgi_la_LIBADD) $(LIBS) -mod_cml.la: $(mod_cml_la_OBJECTS) $(mod_cml_la_DEPENDENCIES) - $(LINK) -rpath $(libdir) $(mod_cml_la_LDFLAGS) $(mod_cml_la_OBJECTS) $(mod_cml_la_LIBADD) $(LIBS) -mod_compress.la: $(mod_compress_la_OBJECTS) $(mod_compress_la_DEPENDENCIES) - $(LINK) -rpath $(libdir) $(mod_compress_la_LDFLAGS) $(mod_compress_la_OBJECTS) $(mod_compress_la_LIBADD) $(LIBS) -mod_dirlisting.la: $(mod_dirlisting_la_OBJECTS) $(mod_dirlisting_la_DEPENDENCIES) - $(LINK) -rpath $(libdir) $(mod_dirlisting_la_LDFLAGS) $(mod_dirlisting_la_OBJECTS) $(mod_dirlisting_la_LIBADD) $(LIBS) -mod_evasive.la: $(mod_evasive_la_OBJECTS) $(mod_evasive_la_DEPENDENCIES) - $(LINK) -rpath $(libdir) $(mod_evasive_la_LDFLAGS) $(mod_evasive_la_OBJECTS) $(mod_evasive_la_LIBADD) $(LIBS) -mod_evhost.la: $(mod_evhost_la_OBJECTS) $(mod_evhost_la_DEPENDENCIES) - $(LINK) -rpath $(libdir) $(mod_evhost_la_LDFLAGS) $(mod_evhost_la_OBJECTS) $(mod_evhost_la_LIBADD) $(LIBS) -mod_expire.la: $(mod_expire_la_OBJECTS) $(mod_expire_la_DEPENDENCIES) - $(LINK) -rpath $(libdir) $(mod_expire_la_LDFLAGS) $(mod_expire_la_OBJECTS) $(mod_expire_la_LIBADD) $(LIBS) -mod_fastcgi.la: $(mod_fastcgi_la_OBJECTS) $(mod_fastcgi_la_DEPENDENCIES) - $(LINK) -rpath $(libdir) $(mod_fastcgi_la_LDFLAGS) $(mod_fastcgi_la_OBJECTS) $(mod_fastcgi_la_LIBADD) $(LIBS) -mod_indexfile.la: $(mod_indexfile_la_OBJECTS) $(mod_indexfile_la_DEPENDENCIES) - $(LINK) -rpath $(libdir) $(mod_indexfile_la_LDFLAGS) $(mod_indexfile_la_OBJECTS) $(mod_indexfile_la_LIBADD) $(LIBS) -mod_mysql_vhost.la: $(mod_mysql_vhost_la_OBJECTS) $(mod_mysql_vhost_la_DEPENDENCIES) - $(LINK) -rpath $(libdir) $(mod_mysql_vhost_la_LDFLAGS) $(mod_mysql_vhost_la_OBJECTS) $(mod_mysql_vhost_la_LIBADD) $(LIBS) -mod_proxy.la: $(mod_proxy_la_OBJECTS) $(mod_proxy_la_DEPENDENCIES) - $(LINK) -rpath $(libdir) $(mod_proxy_la_LDFLAGS) $(mod_proxy_la_OBJECTS) $(mod_proxy_la_LIBADD) $(LIBS) -mod_redirect.la: $(mod_redirect_la_OBJECTS) $(mod_redirect_la_DEPENDENCIES) - $(LINK) -rpath $(libdir) $(mod_redirect_la_LDFLAGS) $(mod_redirect_la_OBJECTS) $(mod_redirect_la_LIBADD) $(LIBS) -mod_rewrite.la: $(mod_rewrite_la_OBJECTS) $(mod_rewrite_la_DEPENDENCIES) - $(LINK) -rpath $(libdir) $(mod_rewrite_la_LDFLAGS) $(mod_rewrite_la_OBJECTS) $(mod_rewrite_la_LIBADD) $(LIBS) -mod_rrdtool.la: $(mod_rrdtool_la_OBJECTS) $(mod_rrdtool_la_DEPENDENCIES) - $(LINK) -rpath $(libdir) $(mod_rrdtool_la_LDFLAGS) $(mod_rrdtool_la_OBJECTS) $(mod_rrdtool_la_LIBADD) $(LIBS) -mod_scgi.la: $(mod_scgi_la_OBJECTS) $(mod_scgi_la_DEPENDENCIES) - $(LINK) -rpath $(libdir) $(mod_scgi_la_LDFLAGS) $(mod_scgi_la_OBJECTS) $(mod_scgi_la_LIBADD) $(LIBS) -mod_secdownload.la: $(mod_secdownload_la_OBJECTS) $(mod_secdownload_la_DEPENDENCIES) - $(LINK) -rpath $(libdir) $(mod_secdownload_la_LDFLAGS) $(mod_secdownload_la_OBJECTS) $(mod_secdownload_la_LIBADD) $(LIBS) -mod_setenv.la: $(mod_setenv_la_OBJECTS) $(mod_setenv_la_DEPENDENCIES) - $(LINK) -rpath $(libdir) $(mod_setenv_la_LDFLAGS) $(mod_setenv_la_OBJECTS) $(mod_setenv_la_LIBADD) $(LIBS) -mod_simple_vhost.la: $(mod_simple_vhost_la_OBJECTS) $(mod_simple_vhost_la_DEPENDENCIES) - $(LINK) -rpath $(libdir) $(mod_simple_vhost_la_LDFLAGS) $(mod_simple_vhost_la_OBJECTS) $(mod_simple_vhost_la_LIBADD) $(LIBS) -mod_ssi.la: $(mod_ssi_la_OBJECTS) $(mod_ssi_la_DEPENDENCIES) - $(LINK) -rpath $(libdir) $(mod_ssi_la_LDFLAGS) $(mod_ssi_la_OBJECTS) $(mod_ssi_la_LIBADD) $(LIBS) -mod_staticfile.la: $(mod_staticfile_la_OBJECTS) $(mod_staticfile_la_DEPENDENCIES) - $(LINK) -rpath $(libdir) $(mod_staticfile_la_LDFLAGS) $(mod_staticfile_la_OBJECTS) $(mod_staticfile_la_LIBADD) $(LIBS) -mod_status.la: $(mod_status_la_OBJECTS) $(mod_status_la_DEPENDENCIES) - $(LINK) -rpath $(libdir) $(mod_status_la_LDFLAGS) $(mod_status_la_OBJECTS) $(mod_status_la_LIBADD) $(LIBS) -mod_trigger_b4_dl.la: $(mod_trigger_b4_dl_la_OBJECTS) $(mod_trigger_b4_dl_la_DEPENDENCIES) - $(LINK) -rpath $(libdir) $(mod_trigger_b4_dl_la_LDFLAGS) $(mod_trigger_b4_dl_la_OBJECTS) $(mod_trigger_b4_dl_la_LIBADD) $(LIBS) -mod_userdir.la: $(mod_userdir_la_OBJECTS) $(mod_userdir_la_DEPENDENCIES) - $(LINK) -rpath $(libdir) $(mod_userdir_la_LDFLAGS) $(mod_userdir_la_OBJECTS) $(mod_userdir_la_LIBADD) $(LIBS) -mod_usertrack.la: $(mod_usertrack_la_OBJECTS) $(mod_usertrack_la_DEPENDENCIES) - $(LINK) -rpath $(libdir) $(mod_usertrack_la_LDFLAGS) $(mod_usertrack_la_OBJECTS) $(mod_usertrack_la_LIBADD) $(LIBS) -mod_webdav.la: $(mod_webdav_la_OBJECTS) $(mod_webdav_la_DEPENDENCIES) - $(LINK) -rpath $(libdir) $(mod_webdav_la_LDFLAGS) $(mod_webdav_la_OBJECTS) $(mod_webdav_la_LIBADD) $(LIBS) -install-binPROGRAMS: $(bin_PROGRAMS) - @$(NORMAL_INSTALL) - test -z "$(bindir)" || $(mkdir_p) "$(DESTDIR)$(bindir)" - @list='$(bin_PROGRAMS)'; for p in $$list; do \ - p1=`echo $$p|sed 's/$(EXEEXT)$$//'`; \ - if test -f $$p \ - || test -f $$p1 \ - ; then \ - f=`echo "$$p1" | sed 's,^.*/,,;$(transform);s/$$/$(EXEEXT)/'`; \ - echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) --mode=install $(binPROGRAMS_INSTALL) '$$p' '$(DESTDIR)$(bindir)/$$f'"; \ - $(INSTALL_PROGRAM_ENV) $(LIBTOOL) --mode=install $(binPROGRAMS_INSTALL) "$$p" "$(DESTDIR)$(bindir)/$$f" || exit 1; \ - else :; fi; \ - done - -uninstall-binPROGRAMS: - @$(NORMAL_UNINSTALL) - @list='$(bin_PROGRAMS)'; for p in $$list; do \ - f=`echo "$$p" | sed 's,^.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/'`; \ - echo " rm -f '$(DESTDIR)$(bindir)/$$f'"; \ - rm -f "$(DESTDIR)$(bindir)/$$f"; \ - done - -clean-binPROGRAMS: - @list='$(bin_PROGRAMS)'; for p in $$list; do \ - f=`echo $$p|sed 's/$(EXEEXT)$$//'`; \ - echo " rm -f $$p $$f"; \ - rm -f $$p $$f ; \ - done +liblightcomp.la: $(liblightcomp_la_OBJECTS) $(liblightcomp_la_DEPENDENCIES) $(EXTRA_liblightcomp_la_DEPENDENCIES) + $(AM_V_CCLD)$(liblightcomp_la_LINK) $(am_liblightcomp_la_rpath) $(liblightcomp_la_OBJECTS) $(liblightcomp_la_LIBADD) $(LIBS) +mod_access.la: $(mod_access_la_OBJECTS) $(mod_access_la_DEPENDENCIES) $(EXTRA_mod_access_la_DEPENDENCIES) + $(AM_V_CCLD)$(mod_access_la_LINK) -rpath $(libdir) $(mod_access_la_OBJECTS) $(mod_access_la_LIBADD) $(LIBS) +mod_accesslog.la: $(mod_accesslog_la_OBJECTS) $(mod_accesslog_la_DEPENDENCIES) $(EXTRA_mod_accesslog_la_DEPENDENCIES) + $(AM_V_CCLD)$(mod_accesslog_la_LINK) -rpath $(libdir) $(mod_accesslog_la_OBJECTS) $(mod_accesslog_la_LIBADD) $(LIBS) +mod_alias.la: $(mod_alias_la_OBJECTS) $(mod_alias_la_DEPENDENCIES) $(EXTRA_mod_alias_la_DEPENDENCIES) + $(AM_V_CCLD)$(mod_alias_la_LINK) -rpath $(libdir) $(mod_alias_la_OBJECTS) $(mod_alias_la_LIBADD) $(LIBS) +mod_auth.la: $(mod_auth_la_OBJECTS) $(mod_auth_la_DEPENDENCIES) $(EXTRA_mod_auth_la_DEPENDENCIES) + $(AM_V_CCLD)$(mod_auth_la_LINK) -rpath $(libdir) $(mod_auth_la_OBJECTS) $(mod_auth_la_LIBADD) $(LIBS) +mod_cgi.la: $(mod_cgi_la_OBJECTS) $(mod_cgi_la_DEPENDENCIES) $(EXTRA_mod_cgi_la_DEPENDENCIES) + $(AM_V_CCLD)$(mod_cgi_la_LINK) -rpath $(libdir) $(mod_cgi_la_OBJECTS) $(mod_cgi_la_LIBADD) $(LIBS) +mod_cml.la: $(mod_cml_la_OBJECTS) $(mod_cml_la_DEPENDENCIES) $(EXTRA_mod_cml_la_DEPENDENCIES) + $(AM_V_CCLD)$(mod_cml_la_LINK) -rpath $(libdir) $(mod_cml_la_OBJECTS) $(mod_cml_la_LIBADD) $(LIBS) +mod_compress.la: $(mod_compress_la_OBJECTS) $(mod_compress_la_DEPENDENCIES) $(EXTRA_mod_compress_la_DEPENDENCIES) + $(AM_V_CCLD)$(mod_compress_la_LINK) -rpath $(libdir) $(mod_compress_la_OBJECTS) $(mod_compress_la_LIBADD) $(LIBS) +mod_dirlisting.la: $(mod_dirlisting_la_OBJECTS) $(mod_dirlisting_la_DEPENDENCIES) $(EXTRA_mod_dirlisting_la_DEPENDENCIES) + $(AM_V_CCLD)$(mod_dirlisting_la_LINK) -rpath $(libdir) $(mod_dirlisting_la_OBJECTS) $(mod_dirlisting_la_LIBADD) $(LIBS) +mod_evasive.la: $(mod_evasive_la_OBJECTS) $(mod_evasive_la_DEPENDENCIES) $(EXTRA_mod_evasive_la_DEPENDENCIES) + $(AM_V_CCLD)$(mod_evasive_la_LINK) -rpath $(libdir) $(mod_evasive_la_OBJECTS) $(mod_evasive_la_LIBADD) $(LIBS) +mod_evhost.la: $(mod_evhost_la_OBJECTS) $(mod_evhost_la_DEPENDENCIES) $(EXTRA_mod_evhost_la_DEPENDENCIES) + $(AM_V_CCLD)$(mod_evhost_la_LINK) -rpath $(libdir) $(mod_evhost_la_OBJECTS) $(mod_evhost_la_LIBADD) $(LIBS) +mod_expire.la: $(mod_expire_la_OBJECTS) $(mod_expire_la_DEPENDENCIES) $(EXTRA_mod_expire_la_DEPENDENCIES) + $(AM_V_CCLD)$(mod_expire_la_LINK) -rpath $(libdir) $(mod_expire_la_OBJECTS) $(mod_expire_la_LIBADD) $(LIBS) +mod_extforward.la: $(mod_extforward_la_OBJECTS) $(mod_extforward_la_DEPENDENCIES) $(EXTRA_mod_extforward_la_DEPENDENCIES) + $(AM_V_CCLD)$(mod_extforward_la_LINK) -rpath $(libdir) $(mod_extforward_la_OBJECTS) $(mod_extforward_la_LIBADD) $(LIBS) +mod_fastcgi.la: $(mod_fastcgi_la_OBJECTS) $(mod_fastcgi_la_DEPENDENCIES) $(EXTRA_mod_fastcgi_la_DEPENDENCIES) + $(AM_V_CCLD)$(mod_fastcgi_la_LINK) -rpath $(libdir) $(mod_fastcgi_la_OBJECTS) $(mod_fastcgi_la_LIBADD) $(LIBS) +mod_flv_streaming.la: $(mod_flv_streaming_la_OBJECTS) $(mod_flv_streaming_la_DEPENDENCIES) $(EXTRA_mod_flv_streaming_la_DEPENDENCIES) + $(AM_V_CCLD)$(mod_flv_streaming_la_LINK) -rpath $(libdir) $(mod_flv_streaming_la_OBJECTS) $(mod_flv_streaming_la_LIBADD) $(LIBS) +mod_indexfile.la: $(mod_indexfile_la_OBJECTS) $(mod_indexfile_la_DEPENDENCIES) $(EXTRA_mod_indexfile_la_DEPENDENCIES) + $(AM_V_CCLD)$(mod_indexfile_la_LINK) -rpath $(libdir) $(mod_indexfile_la_OBJECTS) $(mod_indexfile_la_LIBADD) $(LIBS) +mod_magnet.la: $(mod_magnet_la_OBJECTS) $(mod_magnet_la_DEPENDENCIES) $(EXTRA_mod_magnet_la_DEPENDENCIES) + $(AM_V_CCLD)$(mod_magnet_la_LINK) -rpath $(libdir) $(mod_magnet_la_OBJECTS) $(mod_magnet_la_LIBADD) $(LIBS) +mod_mysql_vhost.la: $(mod_mysql_vhost_la_OBJECTS) $(mod_mysql_vhost_la_DEPENDENCIES) $(EXTRA_mod_mysql_vhost_la_DEPENDENCIES) + $(AM_V_CCLD)$(mod_mysql_vhost_la_LINK) -rpath $(libdir) $(mod_mysql_vhost_la_OBJECTS) $(mod_mysql_vhost_la_LIBADD) $(LIBS) +mod_proxy.la: $(mod_proxy_la_OBJECTS) $(mod_proxy_la_DEPENDENCIES) $(EXTRA_mod_proxy_la_DEPENDENCIES) + $(AM_V_CCLD)$(mod_proxy_la_LINK) -rpath $(libdir) $(mod_proxy_la_OBJECTS) $(mod_proxy_la_LIBADD) $(LIBS) +mod_redirect.la: $(mod_redirect_la_OBJECTS) $(mod_redirect_la_DEPENDENCIES) $(EXTRA_mod_redirect_la_DEPENDENCIES) + $(AM_V_CCLD)$(mod_redirect_la_LINK) -rpath $(libdir) $(mod_redirect_la_OBJECTS) $(mod_redirect_la_LIBADD) $(LIBS) +mod_rewrite.la: $(mod_rewrite_la_OBJECTS) $(mod_rewrite_la_DEPENDENCIES) $(EXTRA_mod_rewrite_la_DEPENDENCIES) + $(AM_V_CCLD)$(mod_rewrite_la_LINK) -rpath $(libdir) $(mod_rewrite_la_OBJECTS) $(mod_rewrite_la_LIBADD) $(LIBS) +mod_rrdtool.la: $(mod_rrdtool_la_OBJECTS) $(mod_rrdtool_la_DEPENDENCIES) $(EXTRA_mod_rrdtool_la_DEPENDENCIES) + $(AM_V_CCLD)$(mod_rrdtool_la_LINK) -rpath $(libdir) $(mod_rrdtool_la_OBJECTS) $(mod_rrdtool_la_LIBADD) $(LIBS) +mod_scgi.la: $(mod_scgi_la_OBJECTS) $(mod_scgi_la_DEPENDENCIES) $(EXTRA_mod_scgi_la_DEPENDENCIES) + $(AM_V_CCLD)$(mod_scgi_la_LINK) -rpath $(libdir) $(mod_scgi_la_OBJECTS) $(mod_scgi_la_LIBADD) $(LIBS) +mod_secdownload.la: $(mod_secdownload_la_OBJECTS) $(mod_secdownload_la_DEPENDENCIES) $(EXTRA_mod_secdownload_la_DEPENDENCIES) + $(AM_V_CCLD)$(mod_secdownload_la_LINK) -rpath $(libdir) $(mod_secdownload_la_OBJECTS) $(mod_secdownload_la_LIBADD) $(LIBS) +mod_setenv.la: $(mod_setenv_la_OBJECTS) $(mod_setenv_la_DEPENDENCIES) $(EXTRA_mod_setenv_la_DEPENDENCIES) + $(AM_V_CCLD)$(mod_setenv_la_LINK) -rpath $(libdir) $(mod_setenv_la_OBJECTS) $(mod_setenv_la_LIBADD) $(LIBS) +mod_simple_vhost.la: $(mod_simple_vhost_la_OBJECTS) $(mod_simple_vhost_la_DEPENDENCIES) $(EXTRA_mod_simple_vhost_la_DEPENDENCIES) + $(AM_V_CCLD)$(mod_simple_vhost_la_LINK) -rpath $(libdir) $(mod_simple_vhost_la_OBJECTS) $(mod_simple_vhost_la_LIBADD) $(LIBS) +mod_ssi.la: $(mod_ssi_la_OBJECTS) $(mod_ssi_la_DEPENDENCIES) $(EXTRA_mod_ssi_la_DEPENDENCIES) + $(AM_V_CCLD)$(mod_ssi_la_LINK) -rpath $(libdir) $(mod_ssi_la_OBJECTS) $(mod_ssi_la_LIBADD) $(LIBS) +mod_staticfile.la: $(mod_staticfile_la_OBJECTS) $(mod_staticfile_la_DEPENDENCIES) $(EXTRA_mod_staticfile_la_DEPENDENCIES) + $(AM_V_CCLD)$(mod_staticfile_la_LINK) -rpath $(libdir) $(mod_staticfile_la_OBJECTS) $(mod_staticfile_la_LIBADD) $(LIBS) +mod_status.la: $(mod_status_la_OBJECTS) $(mod_status_la_DEPENDENCIES) $(EXTRA_mod_status_la_DEPENDENCIES) + $(AM_V_CCLD)$(mod_status_la_LINK) -rpath $(libdir) $(mod_status_la_OBJECTS) $(mod_status_la_LIBADD) $(LIBS) +mod_trigger_b4_dl.la: $(mod_trigger_b4_dl_la_OBJECTS) $(mod_trigger_b4_dl_la_DEPENDENCIES) $(EXTRA_mod_trigger_b4_dl_la_DEPENDENCIES) + $(AM_V_CCLD)$(mod_trigger_b4_dl_la_LINK) -rpath $(libdir) $(mod_trigger_b4_dl_la_OBJECTS) $(mod_trigger_b4_dl_la_LIBADD) $(LIBS) +mod_userdir.la: $(mod_userdir_la_OBJECTS) $(mod_userdir_la_DEPENDENCIES) $(EXTRA_mod_userdir_la_DEPENDENCIES) + $(AM_V_CCLD)$(mod_userdir_la_LINK) -rpath $(libdir) $(mod_userdir_la_OBJECTS) $(mod_userdir_la_LIBADD) $(LIBS) +mod_usertrack.la: $(mod_usertrack_la_OBJECTS) $(mod_usertrack_la_DEPENDENCIES) $(EXTRA_mod_usertrack_la_DEPENDENCIES) + $(AM_V_CCLD)$(mod_usertrack_la_LINK) -rpath $(libdir) $(mod_usertrack_la_OBJECTS) $(mod_usertrack_la_LIBADD) $(LIBS) +mod_webdav.la: $(mod_webdav_la_OBJECTS) $(mod_webdav_la_DEPENDENCIES) $(EXTRA_mod_webdav_la_DEPENDENCIES) + $(AM_V_CCLD)$(mod_webdav_la_LINK) -rpath $(libdir) $(mod_webdav_la_OBJECTS) $(mod_webdav_la_LIBADD) $(LIBS) clean-noinstPROGRAMS: - @list='$(noinst_PROGRAMS)'; for p in $$list; do \ - f=`echo $$p|sed 's/$(EXEEXT)$$//'`; \ - echo " rm -f $$p $$f"; \ - rm -f $$p $$f ; \ - done + @list='$(noinst_PROGRAMS)'; test -n "$$list" || exit 0; \ + echo " rm -f" $$list; \ + rm -f $$list || exit $$?; \ + test -n "$(EXEEXT)" || exit 0; \ + list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ + echo " rm -f" $$list; \ + rm -f $$list install-sbinPROGRAMS: $(sbin_PROGRAMS) @$(NORMAL_INSTALL) - test -z "$(sbindir)" || $(mkdir_p) "$(DESTDIR)$(sbindir)" - @list='$(sbin_PROGRAMS)'; for p in $$list; do \ - p1=`echo $$p|sed 's/$(EXEEXT)$$//'`; \ - if test -f $$p \ - || test -f $$p1 \ - ; then \ - f=`echo "$$p1" | sed 's,^.*/,,;$(transform);s/$$/$(EXEEXT)/'`; \ - echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) --mode=install $(sbinPROGRAMS_INSTALL) '$$p' '$(DESTDIR)$(sbindir)/$$f'"; \ - $(INSTALL_PROGRAM_ENV) $(LIBTOOL) --mode=install $(sbinPROGRAMS_INSTALL) "$$p" "$(DESTDIR)$(sbindir)/$$f" || exit 1; \ - else :; fi; \ - done + @list='$(sbin_PROGRAMS)'; test -n "$(sbindir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(sbindir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(sbindir)" || exit 1; \ + fi; \ + for p in $$list; do echo "$$p $$p"; done | \ + sed 's/$(EXEEXT)$$//' | \ + while read p p1; do if test -f $$p || test -f $$p1; \ + then echo "$$p"; echo "$$p"; else :; fi; \ + done | \ + sed -e 'p;s,.*/,,;n;h' -e 's|.*|.|' \ + -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \ + sed 'N;N;N;s,\n, ,g' | \ + $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \ + { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ + if ($$2 == $$4) files[d] = files[d] " " $$1; \ + else { print "f", $$3 "/" $$4, $$1; } } \ + END { for (d in files) print "f", d, files[d] }' | \ + while read type dir files; do \ + if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ + test -z "$$files" || { \ + echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(sbindir)$$dir'"; \ + $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(sbindir)$$dir" || exit $$?; \ + } \ + ; done uninstall-sbinPROGRAMS: @$(NORMAL_UNINSTALL) - @list='$(sbin_PROGRAMS)'; for p in $$list; do \ - f=`echo "$$p" | sed 's,^.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/'`; \ - echo " rm -f '$(DESTDIR)$(sbindir)/$$f'"; \ - rm -f "$(DESTDIR)$(sbindir)/$$f"; \ - done + @list='$(sbin_PROGRAMS)'; test -n "$(sbindir)" || list=; \ + files=`for p in $$list; do echo "$$p"; done | \ + sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \ + -e 's/$$/$(EXEEXT)/' `; \ + test -n "$$list" || exit 0; \ + echo " ( cd '$(DESTDIR)$(sbindir)' && rm -f" $$files ")"; \ + cd "$(DESTDIR)$(sbindir)" && rm -f $$files clean-sbinPROGRAMS: - @list='$(sbin_PROGRAMS)'; for p in $$list; do \ - f=`echo $$p|sed 's/$(EXEEXT)$$//'`; \ - echo " rm -f $$p $$f"; \ - rm -f $$p $$f ; \ - done -lemon$(EXEEXT): $(lemon_OBJECTS) $(lemon_DEPENDENCIES) + @list='$(sbin_PROGRAMS)'; test -n "$$list" || exit 0; \ + echo " rm -f" $$list; \ + rm -f $$list || exit $$?; \ + test -n "$(EXEEXT)" || exit 0; \ + list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ + echo " rm -f" $$list; \ + rm -f $$list +lemon$(EXEEXT): $(lemon_OBJECTS) $(lemon_DEPENDENCIES) $(EXTRA_lemon_DEPENDENCIES) @rm -f lemon$(EXEEXT) - $(LINK) $(lemon_LDFLAGS) $(lemon_OBJECTS) $(lemon_LDADD) $(LIBS) -lighttpd$(EXEEXT): $(lighttpd_OBJECTS) $(lighttpd_DEPENDENCIES) + $(AM_V_CCLD)$(LINK) $(lemon_OBJECTS) $(lemon_LDADD) $(LIBS) +lighttpd$(EXEEXT): $(lighttpd_OBJECTS) $(lighttpd_DEPENDENCIES) $(EXTRA_lighttpd_DEPENDENCIES) @rm -f lighttpd$(EXEEXT) - $(LINK) $(lighttpd_LDFLAGS) $(lighttpd_OBJECTS) $(lighttpd_LDADD) $(LIBS) -proc_open$(EXEEXT): $(proc_open_OBJECTS) $(proc_open_DEPENDENCIES) + $(AM_V_CCLD)$(lighttpd_LINK) $(lighttpd_OBJECTS) $(lighttpd_LDADD) $(LIBS) +lighttpd-angel$(EXEEXT): $(lighttpd_angel_OBJECTS) $(lighttpd_angel_DEPENDENCIES) $(EXTRA_lighttpd_angel_DEPENDENCIES) + @rm -f lighttpd-angel$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(lighttpd_angel_OBJECTS) $(lighttpd_angel_LDADD) $(LIBS) +proc_open$(EXEEXT): $(proc_open_OBJECTS) $(proc_open_DEPENDENCIES) $(EXTRA_proc_open_DEPENDENCIES) @rm -f proc_open$(EXEEXT) - $(LINK) $(proc_open_LDFLAGS) $(proc_open_OBJECTS) $(proc_open_LDADD) $(LIBS) -spawn-fcgi$(EXEEXT): $(spawn_fcgi_OBJECTS) $(spawn_fcgi_DEPENDENCIES) - @rm -f spawn-fcgi$(EXEEXT) - $(LINK) $(spawn_fcgi_LDFLAGS) $(spawn_fcgi_OBJECTS) $(spawn_fcgi_LDADD) $(LIBS) + $(AM_V_CCLD)$(LINK) $(proc_open_OBJECTS) $(proc_open_LDADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) @@ -845,14 +1073,14 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/etag.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fdevent.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fdevent_freebsd_kqueue.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fdevent_linux_rtsig.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fdevent_libev.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fdevent_linux_sysepoll.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fdevent_poll.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fdevent_select.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fdevent_solaris_devpoll.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fdevent_solaris_port.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/http-header-glue.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/http_auth.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/http_auth_digest.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/http_chunk.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/inet_ntop_cache.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/joblist.Po@am__quote@ @@ -874,11 +1102,12 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblightcomp_la-etag.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblightcomp_la-fdevent.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblightcomp_la-fdevent_freebsd_kqueue.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblightcomp_la-fdevent_linux_rtsig.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblightcomp_la-fdevent_libev.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblightcomp_la-fdevent_linux_sysepoll.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblightcomp_la-fdevent_poll.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblightcomp_la-fdevent_select.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblightcomp_la-fdevent_solaris_devpoll.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblightcomp_la-fdevent_solaris_port.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblightcomp_la-http-header-glue.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblightcomp_la-http_chunk.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblightcomp_la-inet_ntop_cache.Plo@am__quote@ @@ -895,7 +1124,9 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblightcomp_la-plugin.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblightcomp_la-splaytree.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblightcomp_la-stat_cache.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblightcomp_la-status_counter.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblightcomp_la-stream.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lighttpd-angel.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/log.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/md5.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mod_access.Plo@am__quote@ @@ -911,8 +1142,12 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mod_evasive.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mod_evhost.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mod_expire.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mod_extforward.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mod_fastcgi.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mod_flv_streaming.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mod_indexfile.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mod_magnet_la-mod_magnet.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mod_magnet_la-mod_magnet_cache.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mod_mysql_vhost_la-mod_mysql_vhost.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mod_proxy.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mod_redirect.Plo@am__quote@ @@ -945,360 +1180,388 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/request.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/response.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/server.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/spawn-fcgi.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/splaytree.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/stat_cache.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/status_counter.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/stream.Po@am__quote@ .c.o: -@am__fastdepCC_TRUE@ if $(COMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \ -@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi -@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(COMPILE) -c $< +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c $< .c.obj: -@am__fastdepCC_TRUE@ if $(COMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ `$(CYGPATH_W) '$<'`; \ -@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi -@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'` +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c `$(CYGPATH_W) '$<'` .c.lo: -@am__fastdepCC_TRUE@ if $(LTCOMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \ -@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Plo"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi -@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(LTCOMPILE) -c -o $@ $< +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< liblightcomp_la-buffer.lo: buffer.c -@am__fastdepCC_TRUE@ if $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -MT liblightcomp_la-buffer.lo -MD -MP -MF "$(DEPDIR)/liblightcomp_la-buffer.Tpo" -c -o liblightcomp_la-buffer.lo `test -f 'buffer.c' || echo '$(srcdir)/'`buffer.c; \ -@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/liblightcomp_la-buffer.Tpo" "$(DEPDIR)/liblightcomp_la-buffer.Plo"; else rm -f "$(DEPDIR)/liblightcomp_la-buffer.Tpo"; exit 1; fi -@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='buffer.c' object='liblightcomp_la-buffer.lo' libtool=yes @AMDEPBACKSLASH@ +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -MT liblightcomp_la-buffer.lo -MD -MP -MF $(DEPDIR)/liblightcomp_la-buffer.Tpo -c -o liblightcomp_la-buffer.lo `test -f 'buffer.c' || echo '$(srcdir)/'`buffer.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblightcomp_la-buffer.Tpo $(DEPDIR)/liblightcomp_la-buffer.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='buffer.c' object='liblightcomp_la-buffer.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -c -o liblightcomp_la-buffer.lo `test -f 'buffer.c' || echo '$(srcdir)/'`buffer.c +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -c -o liblightcomp_la-buffer.lo `test -f 'buffer.c' || echo '$(srcdir)/'`buffer.c liblightcomp_la-log.lo: log.c -@am__fastdepCC_TRUE@ if $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -MT liblightcomp_la-log.lo -MD -MP -MF "$(DEPDIR)/liblightcomp_la-log.Tpo" -c -o liblightcomp_la-log.lo `test -f 'log.c' || echo '$(srcdir)/'`log.c; \ -@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/liblightcomp_la-log.Tpo" "$(DEPDIR)/liblightcomp_la-log.Plo"; else rm -f "$(DEPDIR)/liblightcomp_la-log.Tpo"; exit 1; fi -@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='log.c' object='liblightcomp_la-log.lo' libtool=yes @AMDEPBACKSLASH@ +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -MT liblightcomp_la-log.lo -MD -MP -MF $(DEPDIR)/liblightcomp_la-log.Tpo -c -o liblightcomp_la-log.lo `test -f 'log.c' || echo '$(srcdir)/'`log.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblightcomp_la-log.Tpo $(DEPDIR)/liblightcomp_la-log.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='log.c' object='liblightcomp_la-log.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -c -o liblightcomp_la-log.lo `test -f 'log.c' || echo '$(srcdir)/'`log.c +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -c -o liblightcomp_la-log.lo `test -f 'log.c' || echo '$(srcdir)/'`log.c liblightcomp_la-keyvalue.lo: keyvalue.c -@am__fastdepCC_TRUE@ if $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -MT liblightcomp_la-keyvalue.lo -MD -MP -MF "$(DEPDIR)/liblightcomp_la-keyvalue.Tpo" -c -o liblightcomp_la-keyvalue.lo `test -f 'keyvalue.c' || echo '$(srcdir)/'`keyvalue.c; \ -@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/liblightcomp_la-keyvalue.Tpo" "$(DEPDIR)/liblightcomp_la-keyvalue.Plo"; else rm -f "$(DEPDIR)/liblightcomp_la-keyvalue.Tpo"; exit 1; fi -@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='keyvalue.c' object='liblightcomp_la-keyvalue.lo' libtool=yes @AMDEPBACKSLASH@ +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -MT liblightcomp_la-keyvalue.lo -MD -MP -MF $(DEPDIR)/liblightcomp_la-keyvalue.Tpo -c -o liblightcomp_la-keyvalue.lo `test -f 'keyvalue.c' || echo '$(srcdir)/'`keyvalue.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblightcomp_la-keyvalue.Tpo $(DEPDIR)/liblightcomp_la-keyvalue.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='keyvalue.c' object='liblightcomp_la-keyvalue.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -c -o liblightcomp_la-keyvalue.lo `test -f 'keyvalue.c' || echo '$(srcdir)/'`keyvalue.c +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -c -o liblightcomp_la-keyvalue.lo `test -f 'keyvalue.c' || echo '$(srcdir)/'`keyvalue.c liblightcomp_la-chunk.lo: chunk.c -@am__fastdepCC_TRUE@ if $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -MT liblightcomp_la-chunk.lo -MD -MP -MF "$(DEPDIR)/liblightcomp_la-chunk.Tpo" -c -o liblightcomp_la-chunk.lo `test -f 'chunk.c' || echo '$(srcdir)/'`chunk.c; \ -@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/liblightcomp_la-chunk.Tpo" "$(DEPDIR)/liblightcomp_la-chunk.Plo"; else rm -f "$(DEPDIR)/liblightcomp_la-chunk.Tpo"; exit 1; fi -@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='chunk.c' object='liblightcomp_la-chunk.lo' libtool=yes @AMDEPBACKSLASH@ +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -MT liblightcomp_la-chunk.lo -MD -MP -MF $(DEPDIR)/liblightcomp_la-chunk.Tpo -c -o liblightcomp_la-chunk.lo `test -f 'chunk.c' || echo '$(srcdir)/'`chunk.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblightcomp_la-chunk.Tpo $(DEPDIR)/liblightcomp_la-chunk.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='chunk.c' object='liblightcomp_la-chunk.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -c -o liblightcomp_la-chunk.lo `test -f 'chunk.c' || echo '$(srcdir)/'`chunk.c +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -c -o liblightcomp_la-chunk.lo `test -f 'chunk.c' || echo '$(srcdir)/'`chunk.c liblightcomp_la-http_chunk.lo: http_chunk.c -@am__fastdepCC_TRUE@ if $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -MT liblightcomp_la-http_chunk.lo -MD -MP -MF "$(DEPDIR)/liblightcomp_la-http_chunk.Tpo" -c -o liblightcomp_la-http_chunk.lo `test -f 'http_chunk.c' || echo '$(srcdir)/'`http_chunk.c; \ -@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/liblightcomp_la-http_chunk.Tpo" "$(DEPDIR)/liblightcomp_la-http_chunk.Plo"; else rm -f "$(DEPDIR)/liblightcomp_la-http_chunk.Tpo"; exit 1; fi -@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='http_chunk.c' object='liblightcomp_la-http_chunk.lo' libtool=yes @AMDEPBACKSLASH@ +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -MT liblightcomp_la-http_chunk.lo -MD -MP -MF $(DEPDIR)/liblightcomp_la-http_chunk.Tpo -c -o liblightcomp_la-http_chunk.lo `test -f 'http_chunk.c' || echo '$(srcdir)/'`http_chunk.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblightcomp_la-http_chunk.Tpo $(DEPDIR)/liblightcomp_la-http_chunk.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='http_chunk.c' object='liblightcomp_la-http_chunk.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -c -o liblightcomp_la-http_chunk.lo `test -f 'http_chunk.c' || echo '$(srcdir)/'`http_chunk.c +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -c -o liblightcomp_la-http_chunk.lo `test -f 'http_chunk.c' || echo '$(srcdir)/'`http_chunk.c liblightcomp_la-stream.lo: stream.c -@am__fastdepCC_TRUE@ if $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -MT liblightcomp_la-stream.lo -MD -MP -MF "$(DEPDIR)/liblightcomp_la-stream.Tpo" -c -o liblightcomp_la-stream.lo `test -f 'stream.c' || echo '$(srcdir)/'`stream.c; \ -@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/liblightcomp_la-stream.Tpo" "$(DEPDIR)/liblightcomp_la-stream.Plo"; else rm -f "$(DEPDIR)/liblightcomp_la-stream.Tpo"; exit 1; fi -@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='stream.c' object='liblightcomp_la-stream.lo' libtool=yes @AMDEPBACKSLASH@ +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -MT liblightcomp_la-stream.lo -MD -MP -MF $(DEPDIR)/liblightcomp_la-stream.Tpo -c -o liblightcomp_la-stream.lo `test -f 'stream.c' || echo '$(srcdir)/'`stream.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblightcomp_la-stream.Tpo $(DEPDIR)/liblightcomp_la-stream.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='stream.c' object='liblightcomp_la-stream.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -c -o liblightcomp_la-stream.lo `test -f 'stream.c' || echo '$(srcdir)/'`stream.c +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -c -o liblightcomp_la-stream.lo `test -f 'stream.c' || echo '$(srcdir)/'`stream.c liblightcomp_la-fdevent.lo: fdevent.c -@am__fastdepCC_TRUE@ if $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -MT liblightcomp_la-fdevent.lo -MD -MP -MF "$(DEPDIR)/liblightcomp_la-fdevent.Tpo" -c -o liblightcomp_la-fdevent.lo `test -f 'fdevent.c' || echo '$(srcdir)/'`fdevent.c; \ -@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/liblightcomp_la-fdevent.Tpo" "$(DEPDIR)/liblightcomp_la-fdevent.Plo"; else rm -f "$(DEPDIR)/liblightcomp_la-fdevent.Tpo"; exit 1; fi -@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='fdevent.c' object='liblightcomp_la-fdevent.lo' libtool=yes @AMDEPBACKSLASH@ +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -MT liblightcomp_la-fdevent.lo -MD -MP -MF $(DEPDIR)/liblightcomp_la-fdevent.Tpo -c -o liblightcomp_la-fdevent.lo `test -f 'fdevent.c' || echo '$(srcdir)/'`fdevent.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblightcomp_la-fdevent.Tpo $(DEPDIR)/liblightcomp_la-fdevent.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='fdevent.c' object='liblightcomp_la-fdevent.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -c -o liblightcomp_la-fdevent.lo `test -f 'fdevent.c' || echo '$(srcdir)/'`fdevent.c +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -c -o liblightcomp_la-fdevent.lo `test -f 'fdevent.c' || echo '$(srcdir)/'`fdevent.c liblightcomp_la-stat_cache.lo: stat_cache.c -@am__fastdepCC_TRUE@ if $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -MT liblightcomp_la-stat_cache.lo -MD -MP -MF "$(DEPDIR)/liblightcomp_la-stat_cache.Tpo" -c -o liblightcomp_la-stat_cache.lo `test -f 'stat_cache.c' || echo '$(srcdir)/'`stat_cache.c; \ -@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/liblightcomp_la-stat_cache.Tpo" "$(DEPDIR)/liblightcomp_la-stat_cache.Plo"; else rm -f "$(DEPDIR)/liblightcomp_la-stat_cache.Tpo"; exit 1; fi -@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='stat_cache.c' object='liblightcomp_la-stat_cache.lo' libtool=yes @AMDEPBACKSLASH@ +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -MT liblightcomp_la-stat_cache.lo -MD -MP -MF $(DEPDIR)/liblightcomp_la-stat_cache.Tpo -c -o liblightcomp_la-stat_cache.lo `test -f 'stat_cache.c' || echo '$(srcdir)/'`stat_cache.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblightcomp_la-stat_cache.Tpo $(DEPDIR)/liblightcomp_la-stat_cache.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='stat_cache.c' object='liblightcomp_la-stat_cache.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -c -o liblightcomp_la-stat_cache.lo `test -f 'stat_cache.c' || echo '$(srcdir)/'`stat_cache.c +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -c -o liblightcomp_la-stat_cache.lo `test -f 'stat_cache.c' || echo '$(srcdir)/'`stat_cache.c liblightcomp_la-plugin.lo: plugin.c -@am__fastdepCC_TRUE@ if $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -MT liblightcomp_la-plugin.lo -MD -MP -MF "$(DEPDIR)/liblightcomp_la-plugin.Tpo" -c -o liblightcomp_la-plugin.lo `test -f 'plugin.c' || echo '$(srcdir)/'`plugin.c; \ -@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/liblightcomp_la-plugin.Tpo" "$(DEPDIR)/liblightcomp_la-plugin.Plo"; else rm -f "$(DEPDIR)/liblightcomp_la-plugin.Tpo"; exit 1; fi -@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='plugin.c' object='liblightcomp_la-plugin.lo' libtool=yes @AMDEPBACKSLASH@ +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -MT liblightcomp_la-plugin.lo -MD -MP -MF $(DEPDIR)/liblightcomp_la-plugin.Tpo -c -o liblightcomp_la-plugin.lo `test -f 'plugin.c' || echo '$(srcdir)/'`plugin.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblightcomp_la-plugin.Tpo $(DEPDIR)/liblightcomp_la-plugin.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='plugin.c' object='liblightcomp_la-plugin.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -c -o liblightcomp_la-plugin.lo `test -f 'plugin.c' || echo '$(srcdir)/'`plugin.c +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -c -o liblightcomp_la-plugin.lo `test -f 'plugin.c' || echo '$(srcdir)/'`plugin.c liblightcomp_la-joblist.lo: joblist.c -@am__fastdepCC_TRUE@ if $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -MT liblightcomp_la-joblist.lo -MD -MP -MF "$(DEPDIR)/liblightcomp_la-joblist.Tpo" -c -o liblightcomp_la-joblist.lo `test -f 'joblist.c' || echo '$(srcdir)/'`joblist.c; \ -@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/liblightcomp_la-joblist.Tpo" "$(DEPDIR)/liblightcomp_la-joblist.Plo"; else rm -f "$(DEPDIR)/liblightcomp_la-joblist.Tpo"; exit 1; fi -@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='joblist.c' object='liblightcomp_la-joblist.lo' libtool=yes @AMDEPBACKSLASH@ +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -MT liblightcomp_la-joblist.lo -MD -MP -MF $(DEPDIR)/liblightcomp_la-joblist.Tpo -c -o liblightcomp_la-joblist.lo `test -f 'joblist.c' || echo '$(srcdir)/'`joblist.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblightcomp_la-joblist.Tpo $(DEPDIR)/liblightcomp_la-joblist.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='joblist.c' object='liblightcomp_la-joblist.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -c -o liblightcomp_la-joblist.lo `test -f 'joblist.c' || echo '$(srcdir)/'`joblist.c +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -c -o liblightcomp_la-joblist.lo `test -f 'joblist.c' || echo '$(srcdir)/'`joblist.c liblightcomp_la-etag.lo: etag.c -@am__fastdepCC_TRUE@ if $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -MT liblightcomp_la-etag.lo -MD -MP -MF "$(DEPDIR)/liblightcomp_la-etag.Tpo" -c -o liblightcomp_la-etag.lo `test -f 'etag.c' || echo '$(srcdir)/'`etag.c; \ -@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/liblightcomp_la-etag.Tpo" "$(DEPDIR)/liblightcomp_la-etag.Plo"; else rm -f "$(DEPDIR)/liblightcomp_la-etag.Tpo"; exit 1; fi -@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='etag.c' object='liblightcomp_la-etag.lo' libtool=yes @AMDEPBACKSLASH@ +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -MT liblightcomp_la-etag.lo -MD -MP -MF $(DEPDIR)/liblightcomp_la-etag.Tpo -c -o liblightcomp_la-etag.lo `test -f 'etag.c' || echo '$(srcdir)/'`etag.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblightcomp_la-etag.Tpo $(DEPDIR)/liblightcomp_la-etag.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='etag.c' object='liblightcomp_la-etag.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -c -o liblightcomp_la-etag.lo `test -f 'etag.c' || echo '$(srcdir)/'`etag.c +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -c -o liblightcomp_la-etag.lo `test -f 'etag.c' || echo '$(srcdir)/'`etag.c liblightcomp_la-array.lo: array.c -@am__fastdepCC_TRUE@ if $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -MT liblightcomp_la-array.lo -MD -MP -MF "$(DEPDIR)/liblightcomp_la-array.Tpo" -c -o liblightcomp_la-array.lo `test -f 'array.c' || echo '$(srcdir)/'`array.c; \ -@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/liblightcomp_la-array.Tpo" "$(DEPDIR)/liblightcomp_la-array.Plo"; else rm -f "$(DEPDIR)/liblightcomp_la-array.Tpo"; exit 1; fi -@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='array.c' object='liblightcomp_la-array.lo' libtool=yes @AMDEPBACKSLASH@ +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -MT liblightcomp_la-array.lo -MD -MP -MF $(DEPDIR)/liblightcomp_la-array.Tpo -c -o liblightcomp_la-array.lo `test -f 'array.c' || echo '$(srcdir)/'`array.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblightcomp_la-array.Tpo $(DEPDIR)/liblightcomp_la-array.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='array.c' object='liblightcomp_la-array.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -c -o liblightcomp_la-array.lo `test -f 'array.c' || echo '$(srcdir)/'`array.c +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -c -o liblightcomp_la-array.lo `test -f 'array.c' || echo '$(srcdir)/'`array.c liblightcomp_la-data_string.lo: data_string.c -@am__fastdepCC_TRUE@ if $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -MT liblightcomp_la-data_string.lo -MD -MP -MF "$(DEPDIR)/liblightcomp_la-data_string.Tpo" -c -o liblightcomp_la-data_string.lo `test -f 'data_string.c' || echo '$(srcdir)/'`data_string.c; \ -@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/liblightcomp_la-data_string.Tpo" "$(DEPDIR)/liblightcomp_la-data_string.Plo"; else rm -f "$(DEPDIR)/liblightcomp_la-data_string.Tpo"; exit 1; fi -@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='data_string.c' object='liblightcomp_la-data_string.lo' libtool=yes @AMDEPBACKSLASH@ +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -MT liblightcomp_la-data_string.lo -MD -MP -MF $(DEPDIR)/liblightcomp_la-data_string.Tpo -c -o liblightcomp_la-data_string.lo `test -f 'data_string.c' || echo '$(srcdir)/'`data_string.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblightcomp_la-data_string.Tpo $(DEPDIR)/liblightcomp_la-data_string.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='data_string.c' object='liblightcomp_la-data_string.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -c -o liblightcomp_la-data_string.lo `test -f 'data_string.c' || echo '$(srcdir)/'`data_string.c +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -c -o liblightcomp_la-data_string.lo `test -f 'data_string.c' || echo '$(srcdir)/'`data_string.c liblightcomp_la-data_count.lo: data_count.c -@am__fastdepCC_TRUE@ if $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -MT liblightcomp_la-data_count.lo -MD -MP -MF "$(DEPDIR)/liblightcomp_la-data_count.Tpo" -c -o liblightcomp_la-data_count.lo `test -f 'data_count.c' || echo '$(srcdir)/'`data_count.c; \ -@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/liblightcomp_la-data_count.Tpo" "$(DEPDIR)/liblightcomp_la-data_count.Plo"; else rm -f "$(DEPDIR)/liblightcomp_la-data_count.Tpo"; exit 1; fi -@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='data_count.c' object='liblightcomp_la-data_count.lo' libtool=yes @AMDEPBACKSLASH@ +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -MT liblightcomp_la-data_count.lo -MD -MP -MF $(DEPDIR)/liblightcomp_la-data_count.Tpo -c -o liblightcomp_la-data_count.lo `test -f 'data_count.c' || echo '$(srcdir)/'`data_count.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblightcomp_la-data_count.Tpo $(DEPDIR)/liblightcomp_la-data_count.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='data_count.c' object='liblightcomp_la-data_count.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -c -o liblightcomp_la-data_count.lo `test -f 'data_count.c' || echo '$(srcdir)/'`data_count.c +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -c -o liblightcomp_la-data_count.lo `test -f 'data_count.c' || echo '$(srcdir)/'`data_count.c liblightcomp_la-data_array.lo: data_array.c -@am__fastdepCC_TRUE@ if $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -MT liblightcomp_la-data_array.lo -MD -MP -MF "$(DEPDIR)/liblightcomp_la-data_array.Tpo" -c -o liblightcomp_la-data_array.lo `test -f 'data_array.c' || echo '$(srcdir)/'`data_array.c; \ -@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/liblightcomp_la-data_array.Tpo" "$(DEPDIR)/liblightcomp_la-data_array.Plo"; else rm -f "$(DEPDIR)/liblightcomp_la-data_array.Tpo"; exit 1; fi -@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='data_array.c' object='liblightcomp_la-data_array.lo' libtool=yes @AMDEPBACKSLASH@ +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -MT liblightcomp_la-data_array.lo -MD -MP -MF $(DEPDIR)/liblightcomp_la-data_array.Tpo -c -o liblightcomp_la-data_array.lo `test -f 'data_array.c' || echo '$(srcdir)/'`data_array.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblightcomp_la-data_array.Tpo $(DEPDIR)/liblightcomp_la-data_array.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='data_array.c' object='liblightcomp_la-data_array.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -c -o liblightcomp_la-data_array.lo `test -f 'data_array.c' || echo '$(srcdir)/'`data_array.c +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -c -o liblightcomp_la-data_array.lo `test -f 'data_array.c' || echo '$(srcdir)/'`data_array.c liblightcomp_la-data_integer.lo: data_integer.c -@am__fastdepCC_TRUE@ if $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -MT liblightcomp_la-data_integer.lo -MD -MP -MF "$(DEPDIR)/liblightcomp_la-data_integer.Tpo" -c -o liblightcomp_la-data_integer.lo `test -f 'data_integer.c' || echo '$(srcdir)/'`data_integer.c; \ -@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/liblightcomp_la-data_integer.Tpo" "$(DEPDIR)/liblightcomp_la-data_integer.Plo"; else rm -f "$(DEPDIR)/liblightcomp_la-data_integer.Tpo"; exit 1; fi -@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='data_integer.c' object='liblightcomp_la-data_integer.lo' libtool=yes @AMDEPBACKSLASH@ +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -MT liblightcomp_la-data_integer.lo -MD -MP -MF $(DEPDIR)/liblightcomp_la-data_integer.Tpo -c -o liblightcomp_la-data_integer.lo `test -f 'data_integer.c' || echo '$(srcdir)/'`data_integer.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblightcomp_la-data_integer.Tpo $(DEPDIR)/liblightcomp_la-data_integer.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='data_integer.c' object='liblightcomp_la-data_integer.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -c -o liblightcomp_la-data_integer.lo `test -f 'data_integer.c' || echo '$(srcdir)/'`data_integer.c +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -c -o liblightcomp_la-data_integer.lo `test -f 'data_integer.c' || echo '$(srcdir)/'`data_integer.c liblightcomp_la-md5.lo: md5.c -@am__fastdepCC_TRUE@ if $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -MT liblightcomp_la-md5.lo -MD -MP -MF "$(DEPDIR)/liblightcomp_la-md5.Tpo" -c -o liblightcomp_la-md5.lo `test -f 'md5.c' || echo '$(srcdir)/'`md5.c; \ -@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/liblightcomp_la-md5.Tpo" "$(DEPDIR)/liblightcomp_la-md5.Plo"; else rm -f "$(DEPDIR)/liblightcomp_la-md5.Tpo"; exit 1; fi -@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='md5.c' object='liblightcomp_la-md5.lo' libtool=yes @AMDEPBACKSLASH@ +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -MT liblightcomp_la-md5.lo -MD -MP -MF $(DEPDIR)/liblightcomp_la-md5.Tpo -c -o liblightcomp_la-md5.lo `test -f 'md5.c' || echo '$(srcdir)/'`md5.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblightcomp_la-md5.Tpo $(DEPDIR)/liblightcomp_la-md5.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='md5.c' object='liblightcomp_la-md5.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -c -o liblightcomp_la-md5.lo `test -f 'md5.c' || echo '$(srcdir)/'`md5.c +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -c -o liblightcomp_la-md5.lo `test -f 'md5.c' || echo '$(srcdir)/'`md5.c liblightcomp_la-data_fastcgi.lo: data_fastcgi.c -@am__fastdepCC_TRUE@ if $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -MT liblightcomp_la-data_fastcgi.lo -MD -MP -MF "$(DEPDIR)/liblightcomp_la-data_fastcgi.Tpo" -c -o liblightcomp_la-data_fastcgi.lo `test -f 'data_fastcgi.c' || echo '$(srcdir)/'`data_fastcgi.c; \ -@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/liblightcomp_la-data_fastcgi.Tpo" "$(DEPDIR)/liblightcomp_la-data_fastcgi.Plo"; else rm -f "$(DEPDIR)/liblightcomp_la-data_fastcgi.Tpo"; exit 1; fi -@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='data_fastcgi.c' object='liblightcomp_la-data_fastcgi.lo' libtool=yes @AMDEPBACKSLASH@ +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -MT liblightcomp_la-data_fastcgi.lo -MD -MP -MF $(DEPDIR)/liblightcomp_la-data_fastcgi.Tpo -c -o liblightcomp_la-data_fastcgi.lo `test -f 'data_fastcgi.c' || echo '$(srcdir)/'`data_fastcgi.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblightcomp_la-data_fastcgi.Tpo $(DEPDIR)/liblightcomp_la-data_fastcgi.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='data_fastcgi.c' object='liblightcomp_la-data_fastcgi.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -c -o liblightcomp_la-data_fastcgi.lo `test -f 'data_fastcgi.c' || echo '$(srcdir)/'`data_fastcgi.c +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -c -o liblightcomp_la-data_fastcgi.lo `test -f 'data_fastcgi.c' || echo '$(srcdir)/'`data_fastcgi.c liblightcomp_la-fdevent_select.lo: fdevent_select.c -@am__fastdepCC_TRUE@ if $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -MT liblightcomp_la-fdevent_select.lo -MD -MP -MF "$(DEPDIR)/liblightcomp_la-fdevent_select.Tpo" -c -o liblightcomp_la-fdevent_select.lo `test -f 'fdevent_select.c' || echo '$(srcdir)/'`fdevent_select.c; \ -@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/liblightcomp_la-fdevent_select.Tpo" "$(DEPDIR)/liblightcomp_la-fdevent_select.Plo"; else rm -f "$(DEPDIR)/liblightcomp_la-fdevent_select.Tpo"; exit 1; fi -@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='fdevent_select.c' object='liblightcomp_la-fdevent_select.lo' libtool=yes @AMDEPBACKSLASH@ +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -MT liblightcomp_la-fdevent_select.lo -MD -MP -MF $(DEPDIR)/liblightcomp_la-fdevent_select.Tpo -c -o liblightcomp_la-fdevent_select.lo `test -f 'fdevent_select.c' || echo '$(srcdir)/'`fdevent_select.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblightcomp_la-fdevent_select.Tpo $(DEPDIR)/liblightcomp_la-fdevent_select.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='fdevent_select.c' object='liblightcomp_la-fdevent_select.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -c -o liblightcomp_la-fdevent_select.lo `test -f 'fdevent_select.c' || echo '$(srcdir)/'`fdevent_select.c +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -c -o liblightcomp_la-fdevent_select.lo `test -f 'fdevent_select.c' || echo '$(srcdir)/'`fdevent_select.c -liblightcomp_la-fdevent_linux_rtsig.lo: fdevent_linux_rtsig.c -@am__fastdepCC_TRUE@ if $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -MT liblightcomp_la-fdevent_linux_rtsig.lo -MD -MP -MF "$(DEPDIR)/liblightcomp_la-fdevent_linux_rtsig.Tpo" -c -o liblightcomp_la-fdevent_linux_rtsig.lo `test -f 'fdevent_linux_rtsig.c' || echo '$(srcdir)/'`fdevent_linux_rtsig.c; \ -@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/liblightcomp_la-fdevent_linux_rtsig.Tpo" "$(DEPDIR)/liblightcomp_la-fdevent_linux_rtsig.Plo"; else rm -f "$(DEPDIR)/liblightcomp_la-fdevent_linux_rtsig.Tpo"; exit 1; fi -@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='fdevent_linux_rtsig.c' object='liblightcomp_la-fdevent_linux_rtsig.lo' libtool=yes @AMDEPBACKSLASH@ +liblightcomp_la-fdevent_libev.lo: fdevent_libev.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -MT liblightcomp_la-fdevent_libev.lo -MD -MP -MF $(DEPDIR)/liblightcomp_la-fdevent_libev.Tpo -c -o liblightcomp_la-fdevent_libev.lo `test -f 'fdevent_libev.c' || echo '$(srcdir)/'`fdevent_libev.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblightcomp_la-fdevent_libev.Tpo $(DEPDIR)/liblightcomp_la-fdevent_libev.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='fdevent_libev.c' object='liblightcomp_la-fdevent_libev.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -c -o liblightcomp_la-fdevent_linux_rtsig.lo `test -f 'fdevent_linux_rtsig.c' || echo '$(srcdir)/'`fdevent_linux_rtsig.c +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -c -o liblightcomp_la-fdevent_libev.lo `test -f 'fdevent_libev.c' || echo '$(srcdir)/'`fdevent_libev.c liblightcomp_la-fdevent_poll.lo: fdevent_poll.c -@am__fastdepCC_TRUE@ if $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -MT liblightcomp_la-fdevent_poll.lo -MD -MP -MF "$(DEPDIR)/liblightcomp_la-fdevent_poll.Tpo" -c -o liblightcomp_la-fdevent_poll.lo `test -f 'fdevent_poll.c' || echo '$(srcdir)/'`fdevent_poll.c; \ -@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/liblightcomp_la-fdevent_poll.Tpo" "$(DEPDIR)/liblightcomp_la-fdevent_poll.Plo"; else rm -f "$(DEPDIR)/liblightcomp_la-fdevent_poll.Tpo"; exit 1; fi -@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='fdevent_poll.c' object='liblightcomp_la-fdevent_poll.lo' libtool=yes @AMDEPBACKSLASH@ +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -MT liblightcomp_la-fdevent_poll.lo -MD -MP -MF $(DEPDIR)/liblightcomp_la-fdevent_poll.Tpo -c -o liblightcomp_la-fdevent_poll.lo `test -f 'fdevent_poll.c' || echo '$(srcdir)/'`fdevent_poll.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblightcomp_la-fdevent_poll.Tpo $(DEPDIR)/liblightcomp_la-fdevent_poll.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='fdevent_poll.c' object='liblightcomp_la-fdevent_poll.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -c -o liblightcomp_la-fdevent_poll.lo `test -f 'fdevent_poll.c' || echo '$(srcdir)/'`fdevent_poll.c +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -c -o liblightcomp_la-fdevent_poll.lo `test -f 'fdevent_poll.c' || echo '$(srcdir)/'`fdevent_poll.c liblightcomp_la-fdevent_linux_sysepoll.lo: fdevent_linux_sysepoll.c -@am__fastdepCC_TRUE@ if $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -MT liblightcomp_la-fdevent_linux_sysepoll.lo -MD -MP -MF "$(DEPDIR)/liblightcomp_la-fdevent_linux_sysepoll.Tpo" -c -o liblightcomp_la-fdevent_linux_sysepoll.lo `test -f 'fdevent_linux_sysepoll.c' || echo '$(srcdir)/'`fdevent_linux_sysepoll.c; \ -@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/liblightcomp_la-fdevent_linux_sysepoll.Tpo" "$(DEPDIR)/liblightcomp_la-fdevent_linux_sysepoll.Plo"; else rm -f "$(DEPDIR)/liblightcomp_la-fdevent_linux_sysepoll.Tpo"; exit 1; fi -@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='fdevent_linux_sysepoll.c' object='liblightcomp_la-fdevent_linux_sysepoll.lo' libtool=yes @AMDEPBACKSLASH@ +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -MT liblightcomp_la-fdevent_linux_sysepoll.lo -MD -MP -MF $(DEPDIR)/liblightcomp_la-fdevent_linux_sysepoll.Tpo -c -o liblightcomp_la-fdevent_linux_sysepoll.lo `test -f 'fdevent_linux_sysepoll.c' || echo '$(srcdir)/'`fdevent_linux_sysepoll.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblightcomp_la-fdevent_linux_sysepoll.Tpo $(DEPDIR)/liblightcomp_la-fdevent_linux_sysepoll.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='fdevent_linux_sysepoll.c' object='liblightcomp_la-fdevent_linux_sysepoll.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -c -o liblightcomp_la-fdevent_linux_sysepoll.lo `test -f 'fdevent_linux_sysepoll.c' || echo '$(srcdir)/'`fdevent_linux_sysepoll.c +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -c -o liblightcomp_la-fdevent_linux_sysepoll.lo `test -f 'fdevent_linux_sysepoll.c' || echo '$(srcdir)/'`fdevent_linux_sysepoll.c liblightcomp_la-fdevent_solaris_devpoll.lo: fdevent_solaris_devpoll.c -@am__fastdepCC_TRUE@ if $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -MT liblightcomp_la-fdevent_solaris_devpoll.lo -MD -MP -MF "$(DEPDIR)/liblightcomp_la-fdevent_solaris_devpoll.Tpo" -c -o liblightcomp_la-fdevent_solaris_devpoll.lo `test -f 'fdevent_solaris_devpoll.c' || echo '$(srcdir)/'`fdevent_solaris_devpoll.c; \ -@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/liblightcomp_la-fdevent_solaris_devpoll.Tpo" "$(DEPDIR)/liblightcomp_la-fdevent_solaris_devpoll.Plo"; else rm -f "$(DEPDIR)/liblightcomp_la-fdevent_solaris_devpoll.Tpo"; exit 1; fi -@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='fdevent_solaris_devpoll.c' object='liblightcomp_la-fdevent_solaris_devpoll.lo' libtool=yes @AMDEPBACKSLASH@ +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -MT liblightcomp_la-fdevent_solaris_devpoll.lo -MD -MP -MF $(DEPDIR)/liblightcomp_la-fdevent_solaris_devpoll.Tpo -c -o liblightcomp_la-fdevent_solaris_devpoll.lo `test -f 'fdevent_solaris_devpoll.c' || echo '$(srcdir)/'`fdevent_solaris_devpoll.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblightcomp_la-fdevent_solaris_devpoll.Tpo $(DEPDIR)/liblightcomp_la-fdevent_solaris_devpoll.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='fdevent_solaris_devpoll.c' object='liblightcomp_la-fdevent_solaris_devpoll.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -c -o liblightcomp_la-fdevent_solaris_devpoll.lo `test -f 'fdevent_solaris_devpoll.c' || echo '$(srcdir)/'`fdevent_solaris_devpoll.c + +liblightcomp_la-fdevent_solaris_port.lo: fdevent_solaris_port.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -MT liblightcomp_la-fdevent_solaris_port.lo -MD -MP -MF $(DEPDIR)/liblightcomp_la-fdevent_solaris_port.Tpo -c -o liblightcomp_la-fdevent_solaris_port.lo `test -f 'fdevent_solaris_port.c' || echo '$(srcdir)/'`fdevent_solaris_port.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblightcomp_la-fdevent_solaris_port.Tpo $(DEPDIR)/liblightcomp_la-fdevent_solaris_port.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='fdevent_solaris_port.c' object='liblightcomp_la-fdevent_solaris_port.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -c -o liblightcomp_la-fdevent_solaris_devpoll.lo `test -f 'fdevent_solaris_devpoll.c' || echo '$(srcdir)/'`fdevent_solaris_devpoll.c +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -c -o liblightcomp_la-fdevent_solaris_port.lo `test -f 'fdevent_solaris_port.c' || echo '$(srcdir)/'`fdevent_solaris_port.c liblightcomp_la-fdevent_freebsd_kqueue.lo: fdevent_freebsd_kqueue.c -@am__fastdepCC_TRUE@ if $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -MT liblightcomp_la-fdevent_freebsd_kqueue.lo -MD -MP -MF "$(DEPDIR)/liblightcomp_la-fdevent_freebsd_kqueue.Tpo" -c -o liblightcomp_la-fdevent_freebsd_kqueue.lo `test -f 'fdevent_freebsd_kqueue.c' || echo '$(srcdir)/'`fdevent_freebsd_kqueue.c; \ -@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/liblightcomp_la-fdevent_freebsd_kqueue.Tpo" "$(DEPDIR)/liblightcomp_la-fdevent_freebsd_kqueue.Plo"; else rm -f "$(DEPDIR)/liblightcomp_la-fdevent_freebsd_kqueue.Tpo"; exit 1; fi -@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='fdevent_freebsd_kqueue.c' object='liblightcomp_la-fdevent_freebsd_kqueue.lo' libtool=yes @AMDEPBACKSLASH@ +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -MT liblightcomp_la-fdevent_freebsd_kqueue.lo -MD -MP -MF $(DEPDIR)/liblightcomp_la-fdevent_freebsd_kqueue.Tpo -c -o liblightcomp_la-fdevent_freebsd_kqueue.lo `test -f 'fdevent_freebsd_kqueue.c' || echo '$(srcdir)/'`fdevent_freebsd_kqueue.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblightcomp_la-fdevent_freebsd_kqueue.Tpo $(DEPDIR)/liblightcomp_la-fdevent_freebsd_kqueue.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='fdevent_freebsd_kqueue.c' object='liblightcomp_la-fdevent_freebsd_kqueue.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -c -o liblightcomp_la-fdevent_freebsd_kqueue.lo `test -f 'fdevent_freebsd_kqueue.c' || echo '$(srcdir)/'`fdevent_freebsd_kqueue.c +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -c -o liblightcomp_la-fdevent_freebsd_kqueue.lo `test -f 'fdevent_freebsd_kqueue.c' || echo '$(srcdir)/'`fdevent_freebsd_kqueue.c liblightcomp_la-data_config.lo: data_config.c -@am__fastdepCC_TRUE@ if $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -MT liblightcomp_la-data_config.lo -MD -MP -MF "$(DEPDIR)/liblightcomp_la-data_config.Tpo" -c -o liblightcomp_la-data_config.lo `test -f 'data_config.c' || echo '$(srcdir)/'`data_config.c; \ -@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/liblightcomp_la-data_config.Tpo" "$(DEPDIR)/liblightcomp_la-data_config.Plo"; else rm -f "$(DEPDIR)/liblightcomp_la-data_config.Tpo"; exit 1; fi -@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='data_config.c' object='liblightcomp_la-data_config.lo' libtool=yes @AMDEPBACKSLASH@ +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -MT liblightcomp_la-data_config.lo -MD -MP -MF $(DEPDIR)/liblightcomp_la-data_config.Tpo -c -o liblightcomp_la-data_config.lo `test -f 'data_config.c' || echo '$(srcdir)/'`data_config.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblightcomp_la-data_config.Tpo $(DEPDIR)/liblightcomp_la-data_config.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='data_config.c' object='liblightcomp_la-data_config.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -c -o liblightcomp_la-data_config.lo `test -f 'data_config.c' || echo '$(srcdir)/'`data_config.c +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -c -o liblightcomp_la-data_config.lo `test -f 'data_config.c' || echo '$(srcdir)/'`data_config.c liblightcomp_la-bitset.lo: bitset.c -@am__fastdepCC_TRUE@ if $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -MT liblightcomp_la-bitset.lo -MD -MP -MF "$(DEPDIR)/liblightcomp_la-bitset.Tpo" -c -o liblightcomp_la-bitset.lo `test -f 'bitset.c' || echo '$(srcdir)/'`bitset.c; \ -@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/liblightcomp_la-bitset.Tpo" "$(DEPDIR)/liblightcomp_la-bitset.Plo"; else rm -f "$(DEPDIR)/liblightcomp_la-bitset.Tpo"; exit 1; fi -@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='bitset.c' object='liblightcomp_la-bitset.lo' libtool=yes @AMDEPBACKSLASH@ +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -MT liblightcomp_la-bitset.lo -MD -MP -MF $(DEPDIR)/liblightcomp_la-bitset.Tpo -c -o liblightcomp_la-bitset.lo `test -f 'bitset.c' || echo '$(srcdir)/'`bitset.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblightcomp_la-bitset.Tpo $(DEPDIR)/liblightcomp_la-bitset.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='bitset.c' object='liblightcomp_la-bitset.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -c -o liblightcomp_la-bitset.lo `test -f 'bitset.c' || echo '$(srcdir)/'`bitset.c +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -c -o liblightcomp_la-bitset.lo `test -f 'bitset.c' || echo '$(srcdir)/'`bitset.c liblightcomp_la-inet_ntop_cache.lo: inet_ntop_cache.c -@am__fastdepCC_TRUE@ if $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -MT liblightcomp_la-inet_ntop_cache.lo -MD -MP -MF "$(DEPDIR)/liblightcomp_la-inet_ntop_cache.Tpo" -c -o liblightcomp_la-inet_ntop_cache.lo `test -f 'inet_ntop_cache.c' || echo '$(srcdir)/'`inet_ntop_cache.c; \ -@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/liblightcomp_la-inet_ntop_cache.Tpo" "$(DEPDIR)/liblightcomp_la-inet_ntop_cache.Plo"; else rm -f "$(DEPDIR)/liblightcomp_la-inet_ntop_cache.Tpo"; exit 1; fi -@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='inet_ntop_cache.c' object='liblightcomp_la-inet_ntop_cache.lo' libtool=yes @AMDEPBACKSLASH@ +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -MT liblightcomp_la-inet_ntop_cache.lo -MD -MP -MF $(DEPDIR)/liblightcomp_la-inet_ntop_cache.Tpo -c -o liblightcomp_la-inet_ntop_cache.lo `test -f 'inet_ntop_cache.c' || echo '$(srcdir)/'`inet_ntop_cache.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblightcomp_la-inet_ntop_cache.Tpo $(DEPDIR)/liblightcomp_la-inet_ntop_cache.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='inet_ntop_cache.c' object='liblightcomp_la-inet_ntop_cache.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -c -o liblightcomp_la-inet_ntop_cache.lo `test -f 'inet_ntop_cache.c' || echo '$(srcdir)/'`inet_ntop_cache.c +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -c -o liblightcomp_la-inet_ntop_cache.lo `test -f 'inet_ntop_cache.c' || echo '$(srcdir)/'`inet_ntop_cache.c liblightcomp_la-crc32.lo: crc32.c -@am__fastdepCC_TRUE@ if $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -MT liblightcomp_la-crc32.lo -MD -MP -MF "$(DEPDIR)/liblightcomp_la-crc32.Tpo" -c -o liblightcomp_la-crc32.lo `test -f 'crc32.c' || echo '$(srcdir)/'`crc32.c; \ -@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/liblightcomp_la-crc32.Tpo" "$(DEPDIR)/liblightcomp_la-crc32.Plo"; else rm -f "$(DEPDIR)/liblightcomp_la-crc32.Tpo"; exit 1; fi -@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='crc32.c' object='liblightcomp_la-crc32.lo' libtool=yes @AMDEPBACKSLASH@ +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -MT liblightcomp_la-crc32.lo -MD -MP -MF $(DEPDIR)/liblightcomp_la-crc32.Tpo -c -o liblightcomp_la-crc32.lo `test -f 'crc32.c' || echo '$(srcdir)/'`crc32.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblightcomp_la-crc32.Tpo $(DEPDIR)/liblightcomp_la-crc32.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='crc32.c' object='liblightcomp_la-crc32.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -c -o liblightcomp_la-crc32.lo `test -f 'crc32.c' || echo '$(srcdir)/'`crc32.c +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -c -o liblightcomp_la-crc32.lo `test -f 'crc32.c' || echo '$(srcdir)/'`crc32.c liblightcomp_la-connections-glue.lo: connections-glue.c -@am__fastdepCC_TRUE@ if $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -MT liblightcomp_la-connections-glue.lo -MD -MP -MF "$(DEPDIR)/liblightcomp_la-connections-glue.Tpo" -c -o liblightcomp_la-connections-glue.lo `test -f 'connections-glue.c' || echo '$(srcdir)/'`connections-glue.c; \ -@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/liblightcomp_la-connections-glue.Tpo" "$(DEPDIR)/liblightcomp_la-connections-glue.Plo"; else rm -f "$(DEPDIR)/liblightcomp_la-connections-glue.Tpo"; exit 1; fi -@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='connections-glue.c' object='liblightcomp_la-connections-glue.lo' libtool=yes @AMDEPBACKSLASH@ +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -MT liblightcomp_la-connections-glue.lo -MD -MP -MF $(DEPDIR)/liblightcomp_la-connections-glue.Tpo -c -o liblightcomp_la-connections-glue.lo `test -f 'connections-glue.c' || echo '$(srcdir)/'`connections-glue.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblightcomp_la-connections-glue.Tpo $(DEPDIR)/liblightcomp_la-connections-glue.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='connections-glue.c' object='liblightcomp_la-connections-glue.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -c -o liblightcomp_la-connections-glue.lo `test -f 'connections-glue.c' || echo '$(srcdir)/'`connections-glue.c +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -c -o liblightcomp_la-connections-glue.lo `test -f 'connections-glue.c' || echo '$(srcdir)/'`connections-glue.c liblightcomp_la-configfile-glue.lo: configfile-glue.c -@am__fastdepCC_TRUE@ if $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -MT liblightcomp_la-configfile-glue.lo -MD -MP -MF "$(DEPDIR)/liblightcomp_la-configfile-glue.Tpo" -c -o liblightcomp_la-configfile-glue.lo `test -f 'configfile-glue.c' || echo '$(srcdir)/'`configfile-glue.c; \ -@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/liblightcomp_la-configfile-glue.Tpo" "$(DEPDIR)/liblightcomp_la-configfile-glue.Plo"; else rm -f "$(DEPDIR)/liblightcomp_la-configfile-glue.Tpo"; exit 1; fi -@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='configfile-glue.c' object='liblightcomp_la-configfile-glue.lo' libtool=yes @AMDEPBACKSLASH@ +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -MT liblightcomp_la-configfile-glue.lo -MD -MP -MF $(DEPDIR)/liblightcomp_la-configfile-glue.Tpo -c -o liblightcomp_la-configfile-glue.lo `test -f 'configfile-glue.c' || echo '$(srcdir)/'`configfile-glue.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblightcomp_la-configfile-glue.Tpo $(DEPDIR)/liblightcomp_la-configfile-glue.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='configfile-glue.c' object='liblightcomp_la-configfile-glue.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -c -o liblightcomp_la-configfile-glue.lo `test -f 'configfile-glue.c' || echo '$(srcdir)/'`configfile-glue.c +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -c -o liblightcomp_la-configfile-glue.lo `test -f 'configfile-glue.c' || echo '$(srcdir)/'`configfile-glue.c liblightcomp_la-http-header-glue.lo: http-header-glue.c -@am__fastdepCC_TRUE@ if $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -MT liblightcomp_la-http-header-glue.lo -MD -MP -MF "$(DEPDIR)/liblightcomp_la-http-header-glue.Tpo" -c -o liblightcomp_la-http-header-glue.lo `test -f 'http-header-glue.c' || echo '$(srcdir)/'`http-header-glue.c; \ -@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/liblightcomp_la-http-header-glue.Tpo" "$(DEPDIR)/liblightcomp_la-http-header-glue.Plo"; else rm -f "$(DEPDIR)/liblightcomp_la-http-header-glue.Tpo"; exit 1; fi -@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='http-header-glue.c' object='liblightcomp_la-http-header-glue.lo' libtool=yes @AMDEPBACKSLASH@ +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -MT liblightcomp_la-http-header-glue.lo -MD -MP -MF $(DEPDIR)/liblightcomp_la-http-header-glue.Tpo -c -o liblightcomp_la-http-header-glue.lo `test -f 'http-header-glue.c' || echo '$(srcdir)/'`http-header-glue.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblightcomp_la-http-header-glue.Tpo $(DEPDIR)/liblightcomp_la-http-header-glue.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='http-header-glue.c' object='liblightcomp_la-http-header-glue.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -c -o liblightcomp_la-http-header-glue.lo `test -f 'http-header-glue.c' || echo '$(srcdir)/'`http-header-glue.c +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -c -o liblightcomp_la-http-header-glue.lo `test -f 'http-header-glue.c' || echo '$(srcdir)/'`http-header-glue.c liblightcomp_la-network_write.lo: network_write.c -@am__fastdepCC_TRUE@ if $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -MT liblightcomp_la-network_write.lo -MD -MP -MF "$(DEPDIR)/liblightcomp_la-network_write.Tpo" -c -o liblightcomp_la-network_write.lo `test -f 'network_write.c' || echo '$(srcdir)/'`network_write.c; \ -@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/liblightcomp_la-network_write.Tpo" "$(DEPDIR)/liblightcomp_la-network_write.Plo"; else rm -f "$(DEPDIR)/liblightcomp_la-network_write.Tpo"; exit 1; fi -@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='network_write.c' object='liblightcomp_la-network_write.lo' libtool=yes @AMDEPBACKSLASH@ +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -MT liblightcomp_la-network_write.lo -MD -MP -MF $(DEPDIR)/liblightcomp_la-network_write.Tpo -c -o liblightcomp_la-network_write.lo `test -f 'network_write.c' || echo '$(srcdir)/'`network_write.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblightcomp_la-network_write.Tpo $(DEPDIR)/liblightcomp_la-network_write.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='network_write.c' object='liblightcomp_la-network_write.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -c -o liblightcomp_la-network_write.lo `test -f 'network_write.c' || echo '$(srcdir)/'`network_write.c +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -c -o liblightcomp_la-network_write.lo `test -f 'network_write.c' || echo '$(srcdir)/'`network_write.c liblightcomp_la-network_linux_sendfile.lo: network_linux_sendfile.c -@am__fastdepCC_TRUE@ if $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -MT liblightcomp_la-network_linux_sendfile.lo -MD -MP -MF "$(DEPDIR)/liblightcomp_la-network_linux_sendfile.Tpo" -c -o liblightcomp_la-network_linux_sendfile.lo `test -f 'network_linux_sendfile.c' || echo '$(srcdir)/'`network_linux_sendfile.c; \ -@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/liblightcomp_la-network_linux_sendfile.Tpo" "$(DEPDIR)/liblightcomp_la-network_linux_sendfile.Plo"; else rm -f "$(DEPDIR)/liblightcomp_la-network_linux_sendfile.Tpo"; exit 1; fi -@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='network_linux_sendfile.c' object='liblightcomp_la-network_linux_sendfile.lo' libtool=yes @AMDEPBACKSLASH@ +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -MT liblightcomp_la-network_linux_sendfile.lo -MD -MP -MF $(DEPDIR)/liblightcomp_la-network_linux_sendfile.Tpo -c -o liblightcomp_la-network_linux_sendfile.lo `test -f 'network_linux_sendfile.c' || echo '$(srcdir)/'`network_linux_sendfile.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblightcomp_la-network_linux_sendfile.Tpo $(DEPDIR)/liblightcomp_la-network_linux_sendfile.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='network_linux_sendfile.c' object='liblightcomp_la-network_linux_sendfile.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -c -o liblightcomp_la-network_linux_sendfile.lo `test -f 'network_linux_sendfile.c' || echo '$(srcdir)/'`network_linux_sendfile.c +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -c -o liblightcomp_la-network_linux_sendfile.lo `test -f 'network_linux_sendfile.c' || echo '$(srcdir)/'`network_linux_sendfile.c liblightcomp_la-network_freebsd_sendfile.lo: network_freebsd_sendfile.c -@am__fastdepCC_TRUE@ if $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -MT liblightcomp_la-network_freebsd_sendfile.lo -MD -MP -MF "$(DEPDIR)/liblightcomp_la-network_freebsd_sendfile.Tpo" -c -o liblightcomp_la-network_freebsd_sendfile.lo `test -f 'network_freebsd_sendfile.c' || echo '$(srcdir)/'`network_freebsd_sendfile.c; \ -@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/liblightcomp_la-network_freebsd_sendfile.Tpo" "$(DEPDIR)/liblightcomp_la-network_freebsd_sendfile.Plo"; else rm -f "$(DEPDIR)/liblightcomp_la-network_freebsd_sendfile.Tpo"; exit 1; fi -@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='network_freebsd_sendfile.c' object='liblightcomp_la-network_freebsd_sendfile.lo' libtool=yes @AMDEPBACKSLASH@ +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -MT liblightcomp_la-network_freebsd_sendfile.lo -MD -MP -MF $(DEPDIR)/liblightcomp_la-network_freebsd_sendfile.Tpo -c -o liblightcomp_la-network_freebsd_sendfile.lo `test -f 'network_freebsd_sendfile.c' || echo '$(srcdir)/'`network_freebsd_sendfile.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblightcomp_la-network_freebsd_sendfile.Tpo $(DEPDIR)/liblightcomp_la-network_freebsd_sendfile.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='network_freebsd_sendfile.c' object='liblightcomp_la-network_freebsd_sendfile.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -c -o liblightcomp_la-network_freebsd_sendfile.lo `test -f 'network_freebsd_sendfile.c' || echo '$(srcdir)/'`network_freebsd_sendfile.c +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -c -o liblightcomp_la-network_freebsd_sendfile.lo `test -f 'network_freebsd_sendfile.c' || echo '$(srcdir)/'`network_freebsd_sendfile.c liblightcomp_la-network_writev.lo: network_writev.c -@am__fastdepCC_TRUE@ if $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -MT liblightcomp_la-network_writev.lo -MD -MP -MF "$(DEPDIR)/liblightcomp_la-network_writev.Tpo" -c -o liblightcomp_la-network_writev.lo `test -f 'network_writev.c' || echo '$(srcdir)/'`network_writev.c; \ -@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/liblightcomp_la-network_writev.Tpo" "$(DEPDIR)/liblightcomp_la-network_writev.Plo"; else rm -f "$(DEPDIR)/liblightcomp_la-network_writev.Tpo"; exit 1; fi -@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='network_writev.c' object='liblightcomp_la-network_writev.lo' libtool=yes @AMDEPBACKSLASH@ +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -MT liblightcomp_la-network_writev.lo -MD -MP -MF $(DEPDIR)/liblightcomp_la-network_writev.Tpo -c -o liblightcomp_la-network_writev.lo `test -f 'network_writev.c' || echo '$(srcdir)/'`network_writev.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblightcomp_la-network_writev.Tpo $(DEPDIR)/liblightcomp_la-network_writev.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='network_writev.c' object='liblightcomp_la-network_writev.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -c -o liblightcomp_la-network_writev.lo `test -f 'network_writev.c' || echo '$(srcdir)/'`network_writev.c +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -c -o liblightcomp_la-network_writev.lo `test -f 'network_writev.c' || echo '$(srcdir)/'`network_writev.c liblightcomp_la-network_solaris_sendfilev.lo: network_solaris_sendfilev.c -@am__fastdepCC_TRUE@ if $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -MT liblightcomp_la-network_solaris_sendfilev.lo -MD -MP -MF "$(DEPDIR)/liblightcomp_la-network_solaris_sendfilev.Tpo" -c -o liblightcomp_la-network_solaris_sendfilev.lo `test -f 'network_solaris_sendfilev.c' || echo '$(srcdir)/'`network_solaris_sendfilev.c; \ -@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/liblightcomp_la-network_solaris_sendfilev.Tpo" "$(DEPDIR)/liblightcomp_la-network_solaris_sendfilev.Plo"; else rm -f "$(DEPDIR)/liblightcomp_la-network_solaris_sendfilev.Tpo"; exit 1; fi -@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='network_solaris_sendfilev.c' object='liblightcomp_la-network_solaris_sendfilev.lo' libtool=yes @AMDEPBACKSLASH@ +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -MT liblightcomp_la-network_solaris_sendfilev.lo -MD -MP -MF $(DEPDIR)/liblightcomp_la-network_solaris_sendfilev.Tpo -c -o liblightcomp_la-network_solaris_sendfilev.lo `test -f 'network_solaris_sendfilev.c' || echo '$(srcdir)/'`network_solaris_sendfilev.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblightcomp_la-network_solaris_sendfilev.Tpo $(DEPDIR)/liblightcomp_la-network_solaris_sendfilev.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='network_solaris_sendfilev.c' object='liblightcomp_la-network_solaris_sendfilev.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -c -o liblightcomp_la-network_solaris_sendfilev.lo `test -f 'network_solaris_sendfilev.c' || echo '$(srcdir)/'`network_solaris_sendfilev.c +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -c -o liblightcomp_la-network_solaris_sendfilev.lo `test -f 'network_solaris_sendfilev.c' || echo '$(srcdir)/'`network_solaris_sendfilev.c liblightcomp_la-network_openssl.lo: network_openssl.c -@am__fastdepCC_TRUE@ if $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -MT liblightcomp_la-network_openssl.lo -MD -MP -MF "$(DEPDIR)/liblightcomp_la-network_openssl.Tpo" -c -o liblightcomp_la-network_openssl.lo `test -f 'network_openssl.c' || echo '$(srcdir)/'`network_openssl.c; \ -@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/liblightcomp_la-network_openssl.Tpo" "$(DEPDIR)/liblightcomp_la-network_openssl.Plo"; else rm -f "$(DEPDIR)/liblightcomp_la-network_openssl.Tpo"; exit 1; fi -@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='network_openssl.c' object='liblightcomp_la-network_openssl.lo' libtool=yes @AMDEPBACKSLASH@ +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -MT liblightcomp_la-network_openssl.lo -MD -MP -MF $(DEPDIR)/liblightcomp_la-network_openssl.Tpo -c -o liblightcomp_la-network_openssl.lo `test -f 'network_openssl.c' || echo '$(srcdir)/'`network_openssl.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblightcomp_la-network_openssl.Tpo $(DEPDIR)/liblightcomp_la-network_openssl.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='network_openssl.c' object='liblightcomp_la-network_openssl.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -c -o liblightcomp_la-network_openssl.lo `test -f 'network_openssl.c' || echo '$(srcdir)/'`network_openssl.c +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -c -o liblightcomp_la-network_openssl.lo `test -f 'network_openssl.c' || echo '$(srcdir)/'`network_openssl.c liblightcomp_la-splaytree.lo: splaytree.c -@am__fastdepCC_TRUE@ if $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -MT liblightcomp_la-splaytree.lo -MD -MP -MF "$(DEPDIR)/liblightcomp_la-splaytree.Tpo" -c -o liblightcomp_la-splaytree.lo `test -f 'splaytree.c' || echo '$(srcdir)/'`splaytree.c; \ -@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/liblightcomp_la-splaytree.Tpo" "$(DEPDIR)/liblightcomp_la-splaytree.Plo"; else rm -f "$(DEPDIR)/liblightcomp_la-splaytree.Tpo"; exit 1; fi -@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='splaytree.c' object='liblightcomp_la-splaytree.lo' libtool=yes @AMDEPBACKSLASH@ +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -MT liblightcomp_la-splaytree.lo -MD -MP -MF $(DEPDIR)/liblightcomp_la-splaytree.Tpo -c -o liblightcomp_la-splaytree.lo `test -f 'splaytree.c' || echo '$(srcdir)/'`splaytree.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblightcomp_la-splaytree.Tpo $(DEPDIR)/liblightcomp_la-splaytree.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='splaytree.c' object='liblightcomp_la-splaytree.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -c -o liblightcomp_la-splaytree.lo `test -f 'splaytree.c' || echo '$(srcdir)/'`splaytree.c +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -c -o liblightcomp_la-splaytree.lo `test -f 'splaytree.c' || echo '$(srcdir)/'`splaytree.c + +liblightcomp_la-status_counter.lo: status_counter.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -MT liblightcomp_la-status_counter.lo -MD -MP -MF $(DEPDIR)/liblightcomp_la-status_counter.Tpo -c -o liblightcomp_la-status_counter.lo `test -f 'status_counter.c' || echo '$(srcdir)/'`status_counter.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblightcomp_la-status_counter.Tpo $(DEPDIR)/liblightcomp_la-status_counter.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='status_counter.c' object='liblightcomp_la-status_counter.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -c -o liblightcomp_la-status_counter.lo `test -f 'status_counter.c' || echo '$(srcdir)/'`status_counter.c mod_cml_la-mod_cml.lo: mod_cml.c -@am__fastdepCC_TRUE@ if $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(mod_cml_la_CFLAGS) $(CFLAGS) -MT mod_cml_la-mod_cml.lo -MD -MP -MF "$(DEPDIR)/mod_cml_la-mod_cml.Tpo" -c -o mod_cml_la-mod_cml.lo `test -f 'mod_cml.c' || echo '$(srcdir)/'`mod_cml.c; \ -@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/mod_cml_la-mod_cml.Tpo" "$(DEPDIR)/mod_cml_la-mod_cml.Plo"; else rm -f "$(DEPDIR)/mod_cml_la-mod_cml.Tpo"; exit 1; fi -@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='mod_cml.c' object='mod_cml_la-mod_cml.lo' libtool=yes @AMDEPBACKSLASH@ +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(mod_cml_la_CFLAGS) $(CFLAGS) -MT mod_cml_la-mod_cml.lo -MD -MP -MF $(DEPDIR)/mod_cml_la-mod_cml.Tpo -c -o mod_cml_la-mod_cml.lo `test -f 'mod_cml.c' || echo '$(srcdir)/'`mod_cml.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/mod_cml_la-mod_cml.Tpo $(DEPDIR)/mod_cml_la-mod_cml.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mod_cml.c' object='mod_cml_la-mod_cml.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(mod_cml_la_CFLAGS) $(CFLAGS) -c -o mod_cml_la-mod_cml.lo `test -f 'mod_cml.c' || echo '$(srcdir)/'`mod_cml.c +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(mod_cml_la_CFLAGS) $(CFLAGS) -c -o mod_cml_la-mod_cml.lo `test -f 'mod_cml.c' || echo '$(srcdir)/'`mod_cml.c mod_cml_la-mod_cml_lua.lo: mod_cml_lua.c -@am__fastdepCC_TRUE@ if $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(mod_cml_la_CFLAGS) $(CFLAGS) -MT mod_cml_la-mod_cml_lua.lo -MD -MP -MF "$(DEPDIR)/mod_cml_la-mod_cml_lua.Tpo" -c -o mod_cml_la-mod_cml_lua.lo `test -f 'mod_cml_lua.c' || echo '$(srcdir)/'`mod_cml_lua.c; \ -@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/mod_cml_la-mod_cml_lua.Tpo" "$(DEPDIR)/mod_cml_la-mod_cml_lua.Plo"; else rm -f "$(DEPDIR)/mod_cml_la-mod_cml_lua.Tpo"; exit 1; fi -@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='mod_cml_lua.c' object='mod_cml_la-mod_cml_lua.lo' libtool=yes @AMDEPBACKSLASH@ +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(mod_cml_la_CFLAGS) $(CFLAGS) -MT mod_cml_la-mod_cml_lua.lo -MD -MP -MF $(DEPDIR)/mod_cml_la-mod_cml_lua.Tpo -c -o mod_cml_la-mod_cml_lua.lo `test -f 'mod_cml_lua.c' || echo '$(srcdir)/'`mod_cml_lua.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/mod_cml_la-mod_cml_lua.Tpo $(DEPDIR)/mod_cml_la-mod_cml_lua.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mod_cml_lua.c' object='mod_cml_la-mod_cml_lua.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(mod_cml_la_CFLAGS) $(CFLAGS) -c -o mod_cml_la-mod_cml_lua.lo `test -f 'mod_cml_lua.c' || echo '$(srcdir)/'`mod_cml_lua.c +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(mod_cml_la_CFLAGS) $(CFLAGS) -c -o mod_cml_la-mod_cml_lua.lo `test -f 'mod_cml_lua.c' || echo '$(srcdir)/'`mod_cml_lua.c mod_cml_la-mod_cml_funcs.lo: mod_cml_funcs.c -@am__fastdepCC_TRUE@ if $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(mod_cml_la_CFLAGS) $(CFLAGS) -MT mod_cml_la-mod_cml_funcs.lo -MD -MP -MF "$(DEPDIR)/mod_cml_la-mod_cml_funcs.Tpo" -c -o mod_cml_la-mod_cml_funcs.lo `test -f 'mod_cml_funcs.c' || echo '$(srcdir)/'`mod_cml_funcs.c; \ -@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/mod_cml_la-mod_cml_funcs.Tpo" "$(DEPDIR)/mod_cml_la-mod_cml_funcs.Plo"; else rm -f "$(DEPDIR)/mod_cml_la-mod_cml_funcs.Tpo"; exit 1; fi -@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='mod_cml_funcs.c' object='mod_cml_la-mod_cml_funcs.lo' libtool=yes @AMDEPBACKSLASH@ +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(mod_cml_la_CFLAGS) $(CFLAGS) -MT mod_cml_la-mod_cml_funcs.lo -MD -MP -MF $(DEPDIR)/mod_cml_la-mod_cml_funcs.Tpo -c -o mod_cml_la-mod_cml_funcs.lo `test -f 'mod_cml_funcs.c' || echo '$(srcdir)/'`mod_cml_funcs.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/mod_cml_la-mod_cml_funcs.Tpo $(DEPDIR)/mod_cml_la-mod_cml_funcs.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mod_cml_funcs.c' object='mod_cml_la-mod_cml_funcs.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(mod_cml_la_CFLAGS) $(CFLAGS) -c -o mod_cml_la-mod_cml_funcs.lo `test -f 'mod_cml_funcs.c' || echo '$(srcdir)/'`mod_cml_funcs.c +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(mod_cml_la_CFLAGS) $(CFLAGS) -c -o mod_cml_la-mod_cml_funcs.lo `test -f 'mod_cml_funcs.c' || echo '$(srcdir)/'`mod_cml_funcs.c + +mod_magnet_la-mod_magnet.lo: mod_magnet.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(mod_magnet_la_CFLAGS) $(CFLAGS) -MT mod_magnet_la-mod_magnet.lo -MD -MP -MF $(DEPDIR)/mod_magnet_la-mod_magnet.Tpo -c -o mod_magnet_la-mod_magnet.lo `test -f 'mod_magnet.c' || echo '$(srcdir)/'`mod_magnet.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/mod_magnet_la-mod_magnet.Tpo $(DEPDIR)/mod_magnet_la-mod_magnet.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mod_magnet.c' object='mod_magnet_la-mod_magnet.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(mod_magnet_la_CFLAGS) $(CFLAGS) -c -o mod_magnet_la-mod_magnet.lo `test -f 'mod_magnet.c' || echo '$(srcdir)/'`mod_magnet.c + +mod_magnet_la-mod_magnet_cache.lo: mod_magnet_cache.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(mod_magnet_la_CFLAGS) $(CFLAGS) -MT mod_magnet_la-mod_magnet_cache.lo -MD -MP -MF $(DEPDIR)/mod_magnet_la-mod_magnet_cache.Tpo -c -o mod_magnet_la-mod_magnet_cache.lo `test -f 'mod_magnet_cache.c' || echo '$(srcdir)/'`mod_magnet_cache.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/mod_magnet_la-mod_magnet_cache.Tpo $(DEPDIR)/mod_magnet_la-mod_magnet_cache.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mod_magnet_cache.c' object='mod_magnet_la-mod_magnet_cache.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(mod_magnet_la_CFLAGS) $(CFLAGS) -c -o mod_magnet_la-mod_magnet_cache.lo `test -f 'mod_magnet_cache.c' || echo '$(srcdir)/'`mod_magnet_cache.c mod_mysql_vhost_la-mod_mysql_vhost.lo: mod_mysql_vhost.c -@am__fastdepCC_TRUE@ if $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mod_mysql_vhost_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT mod_mysql_vhost_la-mod_mysql_vhost.lo -MD -MP -MF "$(DEPDIR)/mod_mysql_vhost_la-mod_mysql_vhost.Tpo" -c -o mod_mysql_vhost_la-mod_mysql_vhost.lo `test -f 'mod_mysql_vhost.c' || echo '$(srcdir)/'`mod_mysql_vhost.c; \ -@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/mod_mysql_vhost_la-mod_mysql_vhost.Tpo" "$(DEPDIR)/mod_mysql_vhost_la-mod_mysql_vhost.Plo"; else rm -f "$(DEPDIR)/mod_mysql_vhost_la-mod_mysql_vhost.Tpo"; exit 1; fi -@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='mod_mysql_vhost.c' object='mod_mysql_vhost_la-mod_mysql_vhost.lo' libtool=yes @AMDEPBACKSLASH@ +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mod_mysql_vhost_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT mod_mysql_vhost_la-mod_mysql_vhost.lo -MD -MP -MF $(DEPDIR)/mod_mysql_vhost_la-mod_mysql_vhost.Tpo -c -o mod_mysql_vhost_la-mod_mysql_vhost.lo `test -f 'mod_mysql_vhost.c' || echo '$(srcdir)/'`mod_mysql_vhost.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/mod_mysql_vhost_la-mod_mysql_vhost.Tpo $(DEPDIR)/mod_mysql_vhost_la-mod_mysql_vhost.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mod_mysql_vhost.c' object='mod_mysql_vhost_la-mod_mysql_vhost.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mod_mysql_vhost_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o mod_mysql_vhost_la-mod_mysql_vhost.lo `test -f 'mod_mysql_vhost.c' || echo '$(srcdir)/'`mod_mysql_vhost.c +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mod_mysql_vhost_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o mod_mysql_vhost_la-mod_mysql_vhost.lo `test -f 'mod_mysql_vhost.c' || echo '$(srcdir)/'`mod_mysql_vhost.c mod_webdav_la-mod_webdav.lo: mod_webdav.c -@am__fastdepCC_TRUE@ if $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(mod_webdav_la_CFLAGS) $(CFLAGS) -MT mod_webdav_la-mod_webdav.lo -MD -MP -MF "$(DEPDIR)/mod_webdav_la-mod_webdav.Tpo" -c -o mod_webdav_la-mod_webdav.lo `test -f 'mod_webdav.c' || echo '$(srcdir)/'`mod_webdav.c; \ -@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/mod_webdav_la-mod_webdav.Tpo" "$(DEPDIR)/mod_webdav_la-mod_webdav.Plo"; else rm -f "$(DEPDIR)/mod_webdav_la-mod_webdav.Tpo"; exit 1; fi -@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='mod_webdav.c' object='mod_webdav_la-mod_webdav.lo' libtool=yes @AMDEPBACKSLASH@ +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(mod_webdav_la_CFLAGS) $(CFLAGS) -MT mod_webdav_la-mod_webdav.lo -MD -MP -MF $(DEPDIR)/mod_webdav_la-mod_webdav.Tpo -c -o mod_webdav_la-mod_webdav.lo `test -f 'mod_webdav.c' || echo '$(srcdir)/'`mod_webdav.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/mod_webdav_la-mod_webdav.Tpo $(DEPDIR)/mod_webdav_la-mod_webdav.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mod_webdav.c' object='mod_webdav_la-mod_webdav.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(mod_webdav_la_CFLAGS) $(CFLAGS) -c -o mod_webdav_la-mod_webdav.lo `test -f 'mod_webdav.c' || echo '$(srcdir)/'`mod_webdav.c +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(mod_webdav_la_CFLAGS) $(CFLAGS) -c -o mod_webdav_la-mod_webdav.lo `test -f 'mod_webdav.c' || echo '$(srcdir)/'`mod_webdav.c proc_open-proc_open.o: proc_open.c -@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(proc_open_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT proc_open-proc_open.o -MD -MP -MF "$(DEPDIR)/proc_open-proc_open.Tpo" -c -o proc_open-proc_open.o `test -f 'proc_open.c' || echo '$(srcdir)/'`proc_open.c; \ -@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/proc_open-proc_open.Tpo" "$(DEPDIR)/proc_open-proc_open.Po"; else rm -f "$(DEPDIR)/proc_open-proc_open.Tpo"; exit 1; fi -@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='proc_open.c' object='proc_open-proc_open.o' libtool=no @AMDEPBACKSLASH@ +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(proc_open_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT proc_open-proc_open.o -MD -MP -MF $(DEPDIR)/proc_open-proc_open.Tpo -c -o proc_open-proc_open.o `test -f 'proc_open.c' || echo '$(srcdir)/'`proc_open.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/proc_open-proc_open.Tpo $(DEPDIR)/proc_open-proc_open.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='proc_open.c' object='proc_open-proc_open.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(proc_open_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o proc_open-proc_open.o `test -f 'proc_open.c' || echo '$(srcdir)/'`proc_open.c +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(proc_open_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o proc_open-proc_open.o `test -f 'proc_open.c' || echo '$(srcdir)/'`proc_open.c proc_open-proc_open.obj: proc_open.c -@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(proc_open_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT proc_open-proc_open.obj -MD -MP -MF "$(DEPDIR)/proc_open-proc_open.Tpo" -c -o proc_open-proc_open.obj `if test -f 'proc_open.c'; then $(CYGPATH_W) 'proc_open.c'; else $(CYGPATH_W) '$(srcdir)/proc_open.c'; fi`; \ -@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/proc_open-proc_open.Tpo" "$(DEPDIR)/proc_open-proc_open.Po"; else rm -f "$(DEPDIR)/proc_open-proc_open.Tpo"; exit 1; fi -@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='proc_open.c' object='proc_open-proc_open.obj' libtool=no @AMDEPBACKSLASH@ +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(proc_open_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT proc_open-proc_open.obj -MD -MP -MF $(DEPDIR)/proc_open-proc_open.Tpo -c -o proc_open-proc_open.obj `if test -f 'proc_open.c'; then $(CYGPATH_W) 'proc_open.c'; else $(CYGPATH_W) '$(srcdir)/proc_open.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/proc_open-proc_open.Tpo $(DEPDIR)/proc_open-proc_open.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='proc_open.c' object='proc_open-proc_open.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(proc_open_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o proc_open-proc_open.obj `if test -f 'proc_open.c'; then $(CYGPATH_W) 'proc_open.c'; else $(CYGPATH_W) '$(srcdir)/proc_open.c'; fi` +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(proc_open_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o proc_open-proc_open.obj `if test -f 'proc_open.c'; then $(CYGPATH_W) 'proc_open.c'; else $(CYGPATH_W) '$(srcdir)/proc_open.c'; fi` proc_open-buffer.o: buffer.c -@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(proc_open_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT proc_open-buffer.o -MD -MP -MF "$(DEPDIR)/proc_open-buffer.Tpo" -c -o proc_open-buffer.o `test -f 'buffer.c' || echo '$(srcdir)/'`buffer.c; \ -@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/proc_open-buffer.Tpo" "$(DEPDIR)/proc_open-buffer.Po"; else rm -f "$(DEPDIR)/proc_open-buffer.Tpo"; exit 1; fi -@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='buffer.c' object='proc_open-buffer.o' libtool=no @AMDEPBACKSLASH@ +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(proc_open_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT proc_open-buffer.o -MD -MP -MF $(DEPDIR)/proc_open-buffer.Tpo -c -o proc_open-buffer.o `test -f 'buffer.c' || echo '$(srcdir)/'`buffer.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/proc_open-buffer.Tpo $(DEPDIR)/proc_open-buffer.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='buffer.c' object='proc_open-buffer.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(proc_open_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o proc_open-buffer.o `test -f 'buffer.c' || echo '$(srcdir)/'`buffer.c +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(proc_open_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o proc_open-buffer.o `test -f 'buffer.c' || echo '$(srcdir)/'`buffer.c proc_open-buffer.obj: buffer.c -@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(proc_open_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT proc_open-buffer.obj -MD -MP -MF "$(DEPDIR)/proc_open-buffer.Tpo" -c -o proc_open-buffer.obj `if test -f 'buffer.c'; then $(CYGPATH_W) 'buffer.c'; else $(CYGPATH_W) '$(srcdir)/buffer.c'; fi`; \ -@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/proc_open-buffer.Tpo" "$(DEPDIR)/proc_open-buffer.Po"; else rm -f "$(DEPDIR)/proc_open-buffer.Tpo"; exit 1; fi -@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='buffer.c' object='proc_open-buffer.obj' libtool=no @AMDEPBACKSLASH@ +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(proc_open_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT proc_open-buffer.obj -MD -MP -MF $(DEPDIR)/proc_open-buffer.Tpo -c -o proc_open-buffer.obj `if test -f 'buffer.c'; then $(CYGPATH_W) 'buffer.c'; else $(CYGPATH_W) '$(srcdir)/buffer.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/proc_open-buffer.Tpo $(DEPDIR)/proc_open-buffer.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='buffer.c' object='proc_open-buffer.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(proc_open_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o proc_open-buffer.obj `if test -f 'buffer.c'; then $(CYGPATH_W) 'buffer.c'; else $(CYGPATH_W) '$(srcdir)/buffer.c'; fi` +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(proc_open_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o proc_open-buffer.obj `if test -f 'buffer.c'; then $(CYGPATH_W) 'buffer.c'; else $(CYGPATH_W) '$(srcdir)/buffer.c'; fi` mostlyclean-libtool: -rm -f *.lo @@ -1306,95 +1569,98 @@ mostlyclean-libtool: clean-libtool: -rm -rf .libs _libs -distclean-libtool: - -rm -f libtool -uninstall-info-am: - ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | \ - $(AWK) ' { files[$$0] = 1; } \ - END { for (i in files) print i; }'`; \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ mkid -fID $$unique tags: TAGS TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ $(TAGS_FILES) $(LISP) - tags=; \ + set x; \ here=`pwd`; \ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | \ - $(AWK) ' { files[$$0] = 1; } \ - END { for (i in files) print i; }'`; \ - if test -z "$(ETAGS_ARGS)$$tags$$unique"; then :; else \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ - $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ - $$tags $$unique; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ fi ctags: CTAGS CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ $(TAGS_FILES) $(LISP) - tags=; \ - here=`pwd`; \ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | \ - $(AWK) ' { files[$$0] = 1; } \ - END { for (i in files) print i; }'`; \ - test -z "$(CTAGS_ARGS)$$tags$$unique" \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ - $$tags $$unique + $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ - && cd $(top_srcdir) \ - && gtags -i $(GTAGS_ARGS) $$here + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(DISTFILES) - @srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; \ - topsrcdirstrip=`echo "$(top_srcdir)" | sed 's|.|.|g'`; \ - list='$(DISTFILES)'; for file in $$list; do \ - case $$file in \ - $(srcdir)/*) file=`echo "$$file" | sed "s|^$$srcdirstrip/||"`;; \ - $(top_srcdir)/*) file=`echo "$$file" | sed "s|^$$topsrcdirstrip/|$(top_builddir)/|"`;; \ - esac; \ + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ - dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \ - if test "$$dir" != "$$file" && test "$$dir" != "."; then \ - dir="/$$dir"; \ - $(mkdir_p) "$(distdir)$$dir"; \ - else \ - dir=''; \ - fi; \ if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ - cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ - cp -pR $$d/$$file $(distdir)$$dir || exit 1; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ - test -f $(distdir)/$$file \ - || cp -p $$d/$$file $(distdir)/$$file \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am -check: check-am +check: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) check-am all-am: Makefile $(LTLIBRARIES) $(PROGRAMS) $(HEADERS) -install-binPROGRAMS: install-libLTLIBRARIES - installdirs: - for dir in "$(DESTDIR)$(libdir)" "$(DESTDIR)$(bindir)" "$(DESTDIR)$(sbindir)"; do \ - test -z "$$dir" || $(mkdir_p) "$$dir"; \ + for dir in "$(DESTDIR)$(libdir)" "$(DESTDIR)$(sbindir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done -install: install-am +install: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am @@ -1404,31 +1670,39 @@ install-am: all-am installcheck: installcheck-am install-strip: - $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ - install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ - `test -z '$(STRIP)' || \ - echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi mostlyclean-generic: clean-generic: + -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES) distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." + -test -z "$(BUILT_SOURCES)" || rm -f $(BUILT_SOURCES) + -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES) clean: clean-am -clean-am: clean-binPROGRAMS clean-generic clean-libLTLIBRARIES \ - clean-libtool clean-noinstPROGRAMS clean-sbinPROGRAMS \ - mostlyclean-am +clean-am: clean-generic clean-libLTLIBRARIES clean-libtool \ + clean-noinstPROGRAMS clean-sbinPROGRAMS mostlyclean-am distclean: distclean-am -rm -rf ./$(DEPDIR) -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ - distclean-libtool distclean-tags + distclean-tags dvi: dvi-am @@ -1436,19 +1710,38 @@ dvi-am: html: html-am +html-am: + info: info-am info-am: install-data-am: -install-exec-am: install-binPROGRAMS install-libLTLIBRARIES \ - install-sbinPROGRAMS +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: install-libLTLIBRARIES install-sbinPROGRAMS + +install-html: install-html-am + +install-html-am: install-info: install-info-am +install-info-am: + install-man: +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + installcheck-am: maintainer-clean: maintainer-clean-am @@ -1469,44 +1762,68 @@ ps: ps-am ps-am: -uninstall-am: uninstall-binPROGRAMS uninstall-info-am \ - uninstall-libLTLIBRARIES uninstall-sbinPROGRAMS - -.PHONY: CTAGS GTAGS all all-am check check-am clean clean-binPROGRAMS \ - clean-generic clean-libLTLIBRARIES clean-libtool \ - clean-noinstPROGRAMS clean-sbinPROGRAMS ctags distclean \ - distclean-compile distclean-generic distclean-libtool \ - distclean-tags distdir dvi dvi-am html html-am info info-am \ - install install-am install-binPROGRAMS install-data \ - install-data-am install-exec install-exec-am install-info \ - install-info-am install-libLTLIBRARIES install-man \ - install-sbinPROGRAMS install-strip installcheck \ +uninstall-am: uninstall-libLTLIBRARIES uninstall-sbinPROGRAMS + +.MAKE: all check install install-am install-strip + +.PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \ + clean-libLTLIBRARIES clean-libtool clean-noinstPROGRAMS \ + clean-sbinPROGRAMS ctags distclean distclean-compile \ + distclean-generic distclean-libtool distclean-tags distdir dvi \ + dvi-am html html-am info info-am install install-am \ + install-data install-data-am install-dvi install-dvi-am \ + install-exec install-exec-am install-html install-html-am \ + install-info install-info-am install-libLTLIBRARIES \ + install-man install-pdf install-pdf-am install-ps \ + install-ps-am install-sbinPROGRAMS install-strip installcheck \ installcheck-am installdirs maintainer-clean \ maintainer-clean-generic mostlyclean mostlyclean-compile \ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ - tags uninstall uninstall-am uninstall-binPROGRAMS \ - uninstall-info-am uninstall-libLTLIBRARIES \ + tags uninstall uninstall-am uninstall-libLTLIBRARIES \ uninstall-sbinPROGRAMS -#simple_fcgi_SOURCES=simple-fcgi.c -#simple_fcgi_LDADD=-lfcgi +.PHONY: versionstamp parsers + +versionstamp: + @test -f versionstamp.h || touch versionstamp.h; \ + REVISION=""; \ + if test -d "$(top_srcdir)/.svn" -a -x "`which svnversion`"; then \ + REVISION="$$(LANG= LC_ALL=C svnversion "$(top_srcdir)" 2>/dev/null || echo exported)"; \ + if test "$$REVISION" = "exported"; then \ + REVISION=""; \ + fi; \ + fi; \ + if test -z "$$REVISION" -a -d "$(top_srcdir)/.git" -a -x "`which git`"; then \ + REVISION="$$(cd "$(top_srcdir)"; LANG= LC_ALL=C git describe --always 2>/dev/null || echo)"; \ + fi; \ + if test -n "$$REVISION"; then \ + echo "#define REPO_VERSION \"-devel-$$REVISION\"" > versionstamp.h.tmp; \ + else \ + echo "#define REPO_VERSION \"\"" > versionstamp.h.tmp; \ + fi; \ + if ! diff versionstamp.h.tmp versionstamp.h >/dev/null 2>/dev/null; then \ + mv versionstamp.h.tmp versionstamp.h; \ + else \ + rm versionstamp.h.tmp; \ + fi -@CROSS_COMPILING_TRUE@configparser.c configparser.h: -@CROSS_COMPILING_TRUE@mod_ssi_exprparser.c mod_ssi_exprparser.h: -@CROSS_COMPILING_FALSE@configparser.y: lemon -@CROSS_COMPILING_FALSE@mod_ssi_exprparser.y: lemon +@CROSS_COMPILING_TRUE@configparser.c configparser.h: +@CROSS_COMPILING_TRUE@mod_ssi_exprparser.c mod_ssi_exprparser.h: -@CROSS_COMPILING_FALSE@configparser.c configparser.h: configparser.y +@CROSS_COMPILING_TRUE@parsers: +@CROSS_COMPILING_FALSE@configparser.h: configparser.c +@CROSS_COMPILING_FALSE@configparser.c: $(srcdir)/configparser.y $(srcdir)/lempar.c lemon$(EXEEXT) @CROSS_COMPILING_FALSE@ rm -f configparser.h @CROSS_COMPILING_FALSE@ $(LEMON) -q $(srcdir)/configparser.y $(srcdir)/lempar.c -@CROSS_COMPILING_FALSE@mod_ssi_exprparser.c mod_ssi_exprparser.h: mod_ssi_exprparser.y +@CROSS_COMPILING_FALSE@mod_ssi_exprparser.h: mod_ssi_exprparser.c +@CROSS_COMPILING_FALSE@mod_ssi_exprparser.c: $(srcdir)/mod_ssi_exprparser.y $(srcdir)/lempar.c lemon$(EXEEXT) @CROSS_COMPILING_FALSE@ rm -f mod_ssi_exprparser.h @CROSS_COMPILING_FALSE@ $(LEMON) -q $(srcdir)/mod_ssi_exprparser.y $(srcdir)/lempar.c -configfile.c: configparser.h -mod_ssi_expr.c: mod_ssi_exprparser.h +@CROSS_COMPILING_FALSE@parsers: configparser.c mod_ssi_exprparser.c + # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: diff --git a/src/SConscript b/src/SConscript new file mode 100644 index 0000000..7565094 --- /dev/null +++ b/src/SConscript @@ -0,0 +1,192 @@ +import os +import re +import types + +Import('env') + +common_src = Split("buffer.c log.c \ + keyvalue.c chunk.c \ + http_chunk.c stream.c fdevent.c \ + stat_cache.c plugin.c joblist.c etag.c array.c \ + data_string.c data_count.c data_array.c \ + data_integer.c md5.c data_fastcgi.c \ + fdevent_select.c fdevent_libev.c \ + fdevent_poll.c fdevent_linux_sysepoll.c \ + fdevent_solaris_devpoll.c fdevent_solaris_port.c \ + fdevent_freebsd_kqueue.c \ + data_config.c bitset.c \ + inet_ntop_cache.c crc32.c \ + connections-glue.c \ + configfile-glue.c \ + http-header-glue.c \ + splaytree.c network_writev.c \ + network_write.c network_linux_sendfile.c \ + network_freebsd_sendfile.c \ + network_solaris_sendfilev.c network_openssl.c \ + status_counter.c \ +") + +src = Split("server.c response.c connections.c network.c \ + configfile.c configparser.c request.c proc_open.c") + +lemon = env.Program('lemon', 'lemon.c') + +configparser = env.Command(['configparser.c', 'configparser.h'], 'configparser.y', '(cd build; ../' + lemon[0].path + ' -q ../$SOURCE ../src/lempar.c; cd ..)') +env.Depends(configparser, lemon) + +mod_ssi_exprparser = env.Command(['mod_ssi_exprparser.c', 'mod_ssi_exprparser.h'], 'mod_ssi_exprparser.y', '(cd build; ../' + lemon[0].path + ' -q ../$SOURCE ../src/lempar.c; cd ..)') +env.Depends(mod_ssi_exprparser, lemon) + +## the modules and how they are built +modules = { + 'mod_access' : { 'src' : [ 'mod_access.c' ] }, + 'mod_alias' : { 'src' : [ 'mod_alias.c' ] }, + 'mod_cgi' : { 'src' : [ 'mod_cgi.c' ] }, + 'mod_fastcgi' : { 'src' : [ 'mod_fastcgi.c' ] }, + 'mod_scgi' : { 'src' : [ 'mod_scgi.c' ] }, + 'mod_extforward' : { 'src' : [ 'mod_extforward.c' ] }, + 'mod_staticfile' : { 'src' : [ 'mod_staticfile.c' ] }, + 'mod_dirlisting' : { 'src' : [ 'mod_dirlisting.c' ], 'lib' : [ env['LIBPCRE'] ] }, + 'mod_indexfile' : { 'src' : [ 'mod_indexfile.c' ] }, + 'mod_setenv' : { 'src' : [ 'mod_setenv.c' ] }, + 'mod_rrdtool' : { 'src' : [ 'mod_rrdtool.c' ] }, + 'mod_usertrack' : { 'src' : [ 'mod_usertrack.c' ] }, + 'mod_proxy' : { 'src' : [ 'mod_proxy.c' ] }, + 'mod_userdir' : { 'src' : [ 'mod_userdir.c' ] }, + 'mod_secdownload' : { 'src' : [ 'mod_secure_download.c' ] }, + 'mod_accesslog' : { 'src' : [ 'mod_accesslog.c' ] }, + 'mod_simple_vhost' : { 'src' : [ 'mod_simple_vhost.c' ] }, + 'mod_evhost' : { 'src' : [ 'mod_evhost.c' ] }, + 'mod_expire' : { 'src' : [ 'mod_expire.c' ] }, + 'mod_status' : { 'src' : [ 'mod_status.c' ] }, + 'mod_compress' : { 'src' : [ 'mod_compress.c' ], 'lib' : [ env['LIBZ'], env['LIBBZ2'] ] }, + 'mod_redirect' : { 'src' : [ 'mod_redirect.c' ], 'lib' : [ env['LIBPCRE'] ] }, + 'mod_rewrite' : { 'src' : [ 'mod_rewrite.c' ], 'lib' : [ env['LIBPCRE'] ] }, + 'mod_auth' : { + 'src' : [ 'mod_auth.c', 'http_auth.c' ], + 'lib' : [ env['LIBCRYPT'], env['LIBLDAP'], env['LIBLBER'] ] }, + 'mod_webdav' : { 'src' : [ 'mod_webdav.c' ], 'lib' : [ env['LIBXML2'], env['LIBSQLITE3'], env['LIBUUID'] ] }, + 'mod_mysql_vhost' : { 'src' : [ 'mod_mysql_vhost.c' ], 'lib' : [ env['LIBMYSQL'] ] }, + 'mod_trigger_b4_dl' : { 'src' : [ 'mod_trigger_b4_dl.c' ], 'lib' : [ env['LIBPCRE'] ] }, + 'mod_cml' : { + 'src' : [ 'mod_cml_lua.c', 'mod_cml.c', 'mod_cml_funcs.c' ], + 'lib' : [ env['LIBPCRE'], env['LIBMEMCACHE'], env['LIBLUA'], env['LIBLUALIB'] ] }, +# 'mod_uploadprogress' : { 'src' : [ 'mod_uploadprogress.c' ] }, + 'mod_evasive' : { 'src' : [ 'mod_evasive.c' ] }, + 'mod_ssi' : { 'src' : [ 'mod_ssi_exprparser.c', 'mod_ssi_expr.c', 'mod_ssi.c' ], 'lib' : [ env['LIBPCRE'] ] }, + 'mod_flv_streaming' : { 'src' : [ 'mod_flv_streaming.c' ] }, + 'mod_magnet' : { 'src' : [ 'mod_magnet.c', 'mod_magnet_cache.c' ], 'lib' : [ env['LIBLUA'] ] }, +} + +staticenv = env.Copy(CPPFLAGS=[ env['CPPFLAGS'], '-DLIGHTTPD_STATIC', '-DOPENSSL_NO_KRB5']) + +## all the core-sources + the modules +staticsrc = src + common_src + +staticlib = env['LIBS'] +staticinit = '' +for module in modules.keys(): + staticsrc += modules[module]['src'] + staticinit += "PLUGIN_INIT(%s)\n"%module + if modules[module].has_key('lib'): + staticlib += modules[module]['lib'] + +open('plugin-static.h', 'w+').write(staticinit) + +## turn all src-files into objects +staticobj = [] +for cfile in staticsrc: + staticobj += [ staticenv.Object('static-' + cfile.replace('.c', ''), cfile) ] + +staticbin = staticenv.Program('lighttpd-semi-static', + staticobj, + LIBS = staticlib + ) + +## you might have to adjust the list of libs and the order for your setup +## this is tricky, be warned +fullstaticlib = [] + +## try to calculate the libs for fullstatic with ldd +## 1. find the lib +## 2. check the deps +## 3. add them to the libs +searchlibs = os.pathsep.join([ '/lib/', '/usr/lib/', '/usr/local/lib/' ]) +lddre = re.compile(r'^\s+lib([^=-]+)(?:-[\.0-9]+)?\.so\.[0-9]+ =>', re.MULTILINE) +for libs in staticlib: + if type(libs) is types.StringType: libs = [ libs ] + for lib in libs: + fullstaticlib += [ lib ] + solibpath = env.WhereIs('lib' + lib + '.so', searchlibs) + fullstaticlib += [ lib ] + if solibpath is None: + continue + + f = os.popen('ldd ' + solibpath, 'r') + for aword in lddre.findall(f.read()): + fullstaticlib += [ aword ] + f.close + + +fullstaticbin = staticenv.Program('lighttpd-static', + staticobj, + LIBS = fullstaticlib, + LINKFLAGS= ['-static'] + ) + +Alias('static', staticbin) +Alias('fullstatic', fullstaticbin) + +implib = 'lighttpd.exe.a' +bin_targets = ['lighttpd'] +bin_linkflags = [ env['LINKFLAGS'] ] +if env['COMMON_LIB'] == 'lib': + common_lib = env.SharedLibrary('liblighttpd', common_src, LINKFLAGS = [ env['LINKFLAGS'], '-Wl,--export-dynamic' ]) +else: + src += common_src + common_lib = [] + if env['COMMON_LIB'] == 'bin': + bin_linkflags += [ '-Wl,--export-all-symbols', '-Wl,--out-implib=build/' + implib ] + bin_targets += [ implib ] + else: + bin_linkflags += [ '-Wl,--export-dynamic' ] + +instbin = env.Program(bin_targets, src, LINKFLAGS = bin_linkflags, LIBS= [ env['LIBS'], common_lib, env['LIBDL'] ]) +env.Depends(instbin, configparser) + +if env['COMMON_LIB'] == 'bin': + common_lib = instbin[1] + +env['SHLIBPREFIX'] = '' +instlib = [] +for module in modules.keys(): + libs = [ common_lib ] + if modules[module].has_key('lib'): + libs += modules[module]['lib'] + instlib += env.SharedLibrary(module, modules[module]['src'], LIBS= [ libs ]) + +inst = [] + +if env['build_dynamic']: + Default(instbin[0], instlib) + inst += env.Install('${sbindir}', instbin[0]) + inst += env.Install('${libdir}', instlib) + if env['COMMON_LIB'] == 'lib': + Default(common_lib) + inst += env.Install('${bindir}', common_lib) + +if env['build_static']: + Default(staticbin) + inst += env.Install('${sbindir}', staticbin) + +if env['build_fullstatic']: + Default(fullstaticbin) + inst += env.Install('${sbindir}', fullstaticbin) + +env.Alias('dynamic', instbin) +# default all to be installed +env.Alias('install', inst) + +pkgdir = '.' +tarname = env['package'] + '-' + env['version'] + diff --git a/src/array.c b/src/array.c index 14afa28..05568b3 100644 --- a/src/array.c +++ b/src/array.c @@ -1,3 +1,6 @@ +#include "array.h" +#include "buffer.h" + #include <string.h> #include <stdio.h> #include <stdlib.h> @@ -6,17 +9,14 @@ #include <errno.h> #include <assert.h> -#include "array.h" -#include "buffer.h" - array *array_init(void) { array *a; - + a = calloc(1, sizeof(*a)); assert(a); - + a->next_power_of_2 = 1; - + return a; } @@ -43,29 +43,29 @@ array *array_init_array(array *src) { void array_free(array *a) { size_t i; if (!a) return; - + if (!a->is_weakref) { for (i = 0; i < a->size; i++) { if (a->data[i]) a->data[i]->free(a->data[i]); } } - + if (a->data) free(a->data); if (a->sorted) free(a->sorted); - + free(a); } void array_reset(array *a) { size_t i; if (!a) return; - + if (!a->is_weakref) { for (i = 0; i < a->used; i++) { a->data[i]->reset(a->data[i]); } } - + a->used = 0; } @@ -84,20 +84,20 @@ data_unset *array_pop(array *a) { static int array_get_index(array *a, const char *key, size_t keylen, int *rndx) { int ndx = -1; int i, pos = 0; - + if (key == NULL) return -1; - + /* try to find the string */ for (i = pos = a->next_power_of_2 / 2; ; i >>= 1) { int cmp; - + if (pos < 0) { pos += i; } else if (pos >= (int)a->used) { pos -= i; } else { cmp = buffer_caseless_compare(key, keylen, a->data[a->sorted[pos]]->key->ptr, a->data[a->sorted[pos]]->key->used); - + if (cmp == 0) { /* found */ ndx = a->sorted[pos]; @@ -110,46 +110,64 @@ static int array_get_index(array *a, const char *key, size_t keylen, int *rndx) } if (i == 0) break; } - + if (rndx) *rndx = pos; - + return ndx; } data_unset *array_get_element(array *a, const char *key) { int ndx; - + if (-1 != (ndx = array_get_index(a, key, strlen(key) + 1, NULL))) { /* found, leave here */ - + return a->data[ndx]; - } - + } + return NULL; } data_unset *array_get_unused_element(array *a, data_type_t t) { data_unset *ds = NULL; - - UNUSED(t); + unsigned int i; - if (a->size == 0) return NULL; - - if (a->used == a->size) return NULL; + for (i = a->used; i < a->size; i++) { + if (a->data[i] && a->data[i]->type == t) { + ds = a->data[i]; - if (a->data[a->used]) { - ds = a->data[a->used]; - - a->data[a->used] = NULL; + /* make empty slot at a->used for next insert */ + a->data[i] = a->data[a->used]; + a->data[a->used] = NULL; + + return ds; + } } - - return ds; + + return NULL; +} + +void array_set_key_value(array *hdrs, const char *key, size_t key_len, const char *value, size_t val_len) { + data_string *ds_dst; + + if (NULL != (ds_dst = (data_string *)array_get_element(hdrs, key))) { + buffer_copy_string_len(ds_dst->value, value, val_len); + return; + } + + if (NULL == (ds_dst = (data_string *)array_get_unused_element(hdrs, TYPE_STRING))) { + ds_dst = data_string_init(); + } + + buffer_copy_string_len(ds_dst->key, key, key_len); + buffer_copy_string_len(ds_dst->value, value, val_len); + array_insert_unique(hdrs, (data_unset *)ds_dst); } /* replace or insert data, return the old one with the same key */ data_unset *array_replace(array *a, data_unset *du) { int ndx; - + if (-1 == (ndx = array_get_index(a, du->key->ptr, du->key->used, NULL))) { array_insert_unique(a, du); return NULL; @@ -164,31 +182,31 @@ int array_insert_unique(array *a, data_unset *str) { int ndx = -1; int pos = 0; size_t j; - + /* generate unique index if neccesary */ if (str->key->used == 0 || str->is_index_key) { buffer_copy_long(str->key, a->unique_ndx++); str->is_index_key = 1; } - + /* try to find the string */ if (-1 != (ndx = array_get_index(a, str->key->ptr, str->key->used, &pos))) { /* found, leave here */ if (a->data[ndx]->type == str->type) { str->insert_dup(a->data[ndx], str); } else { - fprintf(stderr, "a\n"); + SEGFAULT(); } return 0; } - + /* insert */ - + if (a->used+1 > INT_MAX) { /* we can't handle more then INT_MAX entries: see array_get_index() */ return -1; } - + if (a->size == 0) { a->size = 16; a->data = malloc(sizeof(*a->data) * a->size); @@ -204,34 +222,37 @@ int array_insert_unique(array *a, data_unset *str) { assert(a->sorted); for (j = a->used; j < a->size; j++) a->data[j] = NULL; } - + ndx = (int) a->used; - + + /* make sure there is nothing here */ + if (a->data[ndx]) a->data[ndx]->free(a->data[ndx]); + a->data[a->used++] = str; - + if (pos != ndx && - ((pos < 0) || + ((pos < 0) || buffer_caseless_compare(str->key->ptr, str->key->used, a->data[a->sorted[pos]]->key->ptr, a->data[a->sorted[pos]]->key->used) > 0)) { pos++; - } - + } + /* move everything on step to the right */ if (pos != ndx) { memmove(a->sorted + (pos + 1), a->sorted + (pos), (ndx - pos) * sizeof(*a->sorted)); } - + /* insert */ a->sorted[pos] = ndx; - + if (a->next_power_of_2 == (size_t)ndx) a->next_power_of_2 <<= 1; - + return 0; } void array_print_indent(int depth) { int i; for (i = 0; i < depth; i ++) { - fprintf(stderr, " "); + fprintf(stdout, " "); } } @@ -254,7 +275,7 @@ int array_print(array *a, int depth) { size_t i; size_t maxlen; int oneline = 1; - + if (a->used > 5) { oneline = 0; } @@ -275,20 +296,20 @@ int array_print(array *a, int depth) { } } if (oneline) { - fprintf(stderr, "("); + fprintf(stdout, "("); for (i = 0; i < a->used; i++) { data_unset *du = a->data[i]; if (i != 0) { - fprintf(stderr, ", "); + fprintf(stdout, ", "); } du->print(du, depth + 1); } - fprintf(stderr, ")"); + fprintf(stdout, ")"); return 0; } maxlen = array_get_max_key_length(a); - fprintf(stderr, "(\n"); + fprintf(stdout, "(\n"); for (i = 0; i < a->used; i++) { data_unset *du = a->data[i]; array_print_indent(depth + 1); @@ -296,25 +317,25 @@ int array_print(array *a, int depth) { int j; if (i && (i % 5) == 0) { - fprintf(stderr, "# %zd\n", i); + fprintf(stdout, "# %zd\n", i); array_print_indent(depth + 1); } - fprintf(stderr, "\"%s\"", du->key->ptr); + fprintf(stdout, "\"%s\"", du->key->ptr); for (j = maxlen - strlen(du->key->ptr); j > 0; j --) { - fprintf(stderr, " "); + fprintf(stdout, " "); } - fprintf(stderr, " => "); + fprintf(stdout, " => "); } du->print(du, depth + 1); - fprintf(stderr, ",\n"); + fprintf(stdout, ",\n"); } if (!(i && (i - 1 % 5) == 0)) { array_print_indent(depth + 1); - fprintf(stderr, "# %zd\n", i); + fprintf(stdout, "# %zd\n", i); } array_print_indent(depth); - fprintf(stderr, ")"); - + fprintf(stdout, ")"); + return 0; } @@ -323,47 +344,47 @@ int main (int argc, char **argv) { array *a; data_string *ds; data_count *dc; - + UNUSED(argc); UNUSED(argv); a = array_init(); - + ds = data_string_init(); - buffer_copy_string(ds->key, "abc"); - buffer_copy_string(ds->value, "alfrag"); - + buffer_copy_string_len(ds->key, CONST_STR_LEN("abc")); + buffer_copy_string_len(ds->value, CONST_STR_LEN("alfrag")); + array_insert_unique(a, (data_unset *)ds); - + ds = data_string_init(); - buffer_copy_string(ds->key, "abc"); - buffer_copy_string(ds->value, "hameplman"); - + buffer_copy_string_len(ds->key, CONST_STR_LEN("abc")); + buffer_copy_string_len(ds->value, CONST_STR_LEN("hameplman")); + array_insert_unique(a, (data_unset *)ds); - + ds = data_string_init(); - buffer_copy_string(ds->key, "123"); - buffer_copy_string(ds->value, "alfrag"); - + buffer_copy_string_len(ds->key, CONST_STR_LEN("123")); + buffer_copy_string_len(ds->value, CONST_STR_LEN("alfrag")); + array_insert_unique(a, (data_unset *)ds); - + dc = data_count_init(); - buffer_copy_string(dc->key, "def"); - + buffer_copy_string_len(dc->key, CONST_STR_LEN("def")); + array_insert_unique(a, (data_unset *)dc); - + dc = data_count_init(); - buffer_copy_string(dc->key, "def"); - + buffer_copy_string_len(dc->key, CONST_STR_LEN("def")); + array_insert_unique(a, (data_unset *)dc); - + array_print(a, 0); - + array_free(a); - + fprintf(stderr, "%d\n", buffer_caseless_compare(CONST_STR_LEN("Content-Type"), CONST_STR_LEN("Content-type"))); - + return 0; } #endif diff --git a/src/array.h b/src/array.h index 27f27a4..84245c7 100644 --- a/src/array.h +++ b/src/array.h @@ -1,15 +1,18 @@ #ifndef ARRAY_H #define ARRAY_H -#include <stdlib.h> #ifdef HAVE_CONFIG_H -#include "config.h" +# include "config.h" #endif + #ifdef HAVE_PCRE_H # include <pcre.h> #endif + #include "buffer.h" +#include <stdlib.h> + #define DATA_IS_STRING(x) (x->type == TYPE_STRING) typedef enum { TYPE_UNSET, TYPE_STRING, TYPE_COUNT, TYPE_ARRAY, TYPE_INTEGER, TYPE_FASTCGI, TYPE_CONFIG } data_type_t; @@ -29,21 +32,21 @@ typedef struct data_unset { typedef struct { data_unset **data; - + size_t *sorted; - + size_t used; size_t size; - + size_t unique_ndx; - + size_t next_power_of_2; int is_weakref; /* data is weakref, don't bother the data */ } array; typedef struct { DATA_UNSET; - + int count; } data_count; @@ -51,7 +54,7 @@ data_count *data_count_init(void); typedef struct { DATA_UNSET; - + buffer *value; } data_string; @@ -60,21 +63,44 @@ data_string *data_response_init(void); typedef struct { DATA_UNSET; - + array *value; } data_array; data_array *data_array_init(void); -typedef enum { CONFIG_COND_UNSET, CONFIG_COND_EQ, CONFIG_COND_MATCH, CONFIG_COND_NE, CONFIG_COND_NOMATCH } config_cond_t; - -#define PATCHES NULL, "SERVERsocket", "HTTPurl", "HTTPhost", "HTTPreferer", "HTTPuseragent", "HTTPcookie", "HTTPremoteip" +/** + * possible compare ops in the configfile parser + */ +typedef enum { + CONFIG_COND_UNSET, + CONFIG_COND_EQ, /** == */ + CONFIG_COND_MATCH, /** =~ */ + CONFIG_COND_NE, /** != */ + CONFIG_COND_NOMATCH /** !~ */ +} config_cond_t; + +/** + * possible fields to match against + */ typedef enum { COMP_UNSET, - COMP_SERVER_SOCKET, COMP_HTTP_URL, COMP_HTTP_HOST, COMP_HTTP_REFERER, COMP_HTTP_USERAGENT, COMP_HTTP_COOKIE, COMP_HTTP_REMOTEIP + COMP_SERVER_SOCKET, + COMP_HTTP_URL, + COMP_HTTP_HOST, + COMP_HTTP_REFERER, + COMP_HTTP_USER_AGENT, + COMP_HTTP_LANGUAGE, + COMP_HTTP_COOKIE, + COMP_HTTP_REMOTE_IP, + COMP_HTTP_QUERY_STRING, + COMP_HTTP_SCHEME, + COMP_HTTP_REQUEST_METHOD, + + COMP_LAST_ELEMENT } comp_key_t; -/* $HTTP["host"] == "incremental.home.kneschke.de" { ... } +/* $HTTP["host"] == "incremental.home.kneschke.de" { ... } * for print: comp_key op string * for compare: comp cond string/regex */ @@ -82,15 +108,15 @@ typedef enum { typedef struct _data_config data_config; struct _data_config { DATA_UNSET; - + array *value; - + buffer *comp_key; comp_key_t comp; - + config_cond_t cond; buffer *op; - + int context_ndx; /* more or less like an id */ array *childs; /* nested */ @@ -98,7 +124,7 @@ struct _data_config { /* for chaining only */ data_config *prev; data_config *next; - + buffer *string; #ifdef HAVE_PCRE_H pcre *regex; @@ -110,7 +136,7 @@ data_config *data_config_init(void); typedef struct { DATA_UNSET; - + int value; } data_integer; @@ -120,13 +146,13 @@ typedef struct { DATA_UNSET; buffer *host; - + unsigned short port; time_t disable_ts; int is_disabled; size_t balance; - + int usage; /* fair-balancing needs the no. of connections active on this host */ int last_used_ndx; /* round robin */ } data_fastcgi; @@ -142,6 +168,7 @@ data_unset *array_pop(array *a); int array_print(array *a, int depth); data_unset *array_get_unused_element(array *a, data_type_t t); data_unset *array_get_element(array *a, const char *key); +void array_set_key_value(array *hdrs, const char *key, size_t key_len, const char *value, size_t val_len); data_unset *array_replace(array *a, data_unset *du); int array_strcasecmp(const char *a, size_t a_len, const char *b, size_t b_len); void array_print_indent(int depth); @@ -1,18 +1,21 @@ #ifndef _BASE_H_ #define _BASE_H_ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#include "settings.h" + #include <sys/types.h> #include <sys/time.h> #include <sys/stat.h> -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - #include <limits.h> + #ifdef HAVE_STDINT_H # include <stdint.h> #endif + #ifdef HAVE_INTTYPES_H # include <inttypes.h> #endif @@ -21,15 +24,18 @@ #include "array.h" #include "chunk.h" #include "keyvalue.h" -#include "settings.h" #include "fdevent.h" #include "sys-socket.h" #include "splaytree.h" +#include "etag.h" #if defined HAVE_LIBSSL && defined HAVE_OPENSSL_SSL_H # define USE_OPENSSL -# include <openssl/ssl.h> +# include <openssl/ssl.h> +# if ! defined OPENSSL_NO_TLSEXT && ! defined SSL_CTRL_SET_TLSEXT_HOSTNAME +# define OPENSSL_NO_TLSEXT +# endif #endif #ifdef HAVE_FAM_H @@ -78,26 +84,26 @@ typedef int socklen_t; # define SHUT_WR 1 #endif -#include "settings.h" - -typedef enum { T_CONFIG_UNSET, - T_CONFIG_STRING, - T_CONFIG_SHORT, - T_CONFIG_BOOLEAN, - T_CONFIG_ARRAY, - T_CONFIG_LOCAL, - T_CONFIG_DEPRECATED +typedef enum { T_CONFIG_UNSET, + T_CONFIG_STRING, + T_CONFIG_SHORT, + T_CONFIG_INT, + T_CONFIG_BOOLEAN, + T_CONFIG_ARRAY, + T_CONFIG_LOCAL, + T_CONFIG_DEPRECATED, + T_CONFIG_UNSUPPORTED } config_values_type_t; -typedef enum { T_CONFIG_SCOPE_UNSET, - T_CONFIG_SCOPE_SERVER, +typedef enum { T_CONFIG_SCOPE_UNSET, + T_CONFIG_SCOPE_SERVER, T_CONFIG_SCOPE_CONNECTION } config_scope_type_t; typedef struct { const char *key; void *destination; - + config_values_type_t type; config_scope_type_t scope; } config_values_t; @@ -142,29 +148,29 @@ typedef struct { /* the request-line */ buffer *request; buffer *uri; - + buffer *orig_uri; - + http_method_t http_method; http_version_t http_version; - + buffer *request_line; - + /* strings to the header */ buffer *http_host; /* not alloced */ const char *http_range; const char *http_content_type; const char *http_if_modified_since; const char *http_if_none_match; - + array *headers; - + /* CONTENT */ size_t content_length; /* returned by strtoul() */ - + /* internal representation */ int accept_encoding; - + /* internal */ buffer *pathinfo; } request; @@ -172,40 +178,48 @@ typedef struct { typedef struct { off_t content_length; int keep_alive; /* used by the subrequests in proxy, cgi and fcgi to say the subrequest was keep-alive or not */ - + array *headers; - - enum { + + enum { HTTP_TRANSFER_ENCODING_IDENTITY, HTTP_TRANSFER_ENCODING_CHUNKED } transfer_encoding; } response; typedef struct { - buffer *scheme; + buffer *scheme; /* scheme without colon or slashes ( "http" or "https" ) */ + + /* authority with optional portnumber ("site.name" or "site.name:8080" ) NOTE: without "username:password@" */ buffer *authority; + + /* path including leading slash ("/" or "/index.html") - urldecoded, and sanitized ( buffer_path_simplify() && buffer_urldecode_path() ) */ buffer *path; - buffer *path_raw; - buffer *query; + buffer *path_raw; /* raw path, as sent from client. no urldecoding or path simplifying */ + buffer *query; /* querystring ( everything after "?", ie: in "/index.php?foo=1", query is "foo=1" ) */ } request_uri; typedef struct { buffer *path; buffer *basedir; /* path = "(basedir)(.*)" */ - + buffer *doc_root; /* path = doc_root + rel_path */ buffer *rel_path; - + buffer *etag; } physical; typedef struct { buffer *name; buffer *etag; - + struct stat st; - + time_t stat_ts; - + +#ifdef HAVE_LSTAT + char is_symlink; +#endif + #ifdef HAVE_FAM_H int dir_version; int dir_ndx; @@ -216,7 +230,7 @@ typedef struct { typedef struct { splay_tree *files; /* the nodes of the tree are stat_cache_entry's */ - + buffer *dir_name; /* for building the dirname from the filename */ #ifdef HAVE_FAM_H splay_tree *dirs; /* the nodes of the tree are fam_dir_entry */ @@ -224,11 +238,12 @@ typedef struct { FAMConnection *fam; int fam_fcce_ndx; #endif + buffer *hash_key; /* temp-store for the hash-key */ } stat_cache; typedef struct { array *mimetypes; - + /* virtual-servers */ buffer *document_root; buffer *server_name; @@ -236,7 +251,7 @@ typedef struct { buffer *server_tag; buffer *dirlist_encoding; buffer *errorfile_prefix; - + unsigned short max_keep_alive_requests; unsigned short max_keep_alive_idle; unsigned short max_read_idle; @@ -244,33 +259,52 @@ typedef struct { unsigned short use_xattr; unsigned short follow_symlink; unsigned short range_requests; - + /* debug */ - + unsigned short log_file_not_found; unsigned short log_request_header; unsigned short log_request_handling; unsigned short log_response_header; unsigned short log_condition_handling; - - + unsigned short log_ssl_noise; + unsigned short log_timeouts; + + /* server wide */ buffer *ssl_pemfile; buffer *ssl_ca_file; - unsigned short use_ipv6; + buffer *ssl_cipher_list; + buffer *ssl_dh_file; + buffer *ssl_ec_curve; + unsigned short ssl_honor_cipher_order; /* determine SSL cipher in server-preferred order, not client-order */ + unsigned short ssl_use_sslv2; + unsigned short ssl_use_sslv3; + unsigned short ssl_verifyclient; + unsigned short ssl_verifyclient_enforce; + unsigned short ssl_verifyclient_depth; + buffer *ssl_verifyclient_username; + unsigned short ssl_verifyclient_export_cert; + unsigned short ssl_disable_client_renegotiation; + + unsigned short use_ipv6, set_v6only; /* set_v6only is only a temporary option */ + unsigned short defer_accept; unsigned short is_ssl; unsigned short allow_http11; + unsigned short etag_use_inode; + unsigned short etag_use_mtime; + unsigned short etag_use_size; unsigned short force_lowercase_filenames; /* if the FS is case-insensitive, force all files to lower-case */ - unsigned short max_request_size; + unsigned int max_request_size; unsigned short kbytes_per_second; /* connection kb/s limit */ /* configside */ unsigned short global_kbytes_per_second; /* */ - off_t global_bytes_per_second_cnt; + off_t global_bytes_per_second_cnt; /* server-wide traffic-shaper - * + * * each context has the counter which is inited once * a second by the global_kbytes_per_second config-var * @@ -278,12 +312,12 @@ typedef struct { * the connected conns are "offline" a little bit * * the problem: - * we somehow have to loose our "we are writable" signal + * we somehow have to loose our "we are writable" signal * on the way. - * + * */ off_t *global_bytes_per_second_cnt_ptr; /* */ - + #ifdef USE_OPENSSL SSL_CTX *ssl_ctx; #endif @@ -291,18 +325,18 @@ typedef struct { /* the order of the items should be the same as they are processed * read before write as we use this later */ -typedef enum { - CON_STATE_CONNECT, - CON_STATE_REQUEST_START, - CON_STATE_READ, - CON_STATE_REQUEST_END, - CON_STATE_READ_POST, - CON_STATE_HANDLE_REQUEST, - CON_STATE_RESPONSE_START, - CON_STATE_WRITE, - CON_STATE_RESPONSE_END, - CON_STATE_ERROR, - CON_STATE_CLOSE +typedef enum { + CON_STATE_CONNECT, + CON_STATE_REQUEST_START, + CON_STATE_READ, + CON_STATE_REQUEST_END, + CON_STATE_READ_POST, + CON_STATE_HANDLE_REQUEST, + CON_STATE_RESPONSE_START, + CON_STATE_WRITE, + CON_STATE_RESPONSE_END, + CON_STATE_ERROR, + CON_STATE_CLOSE } connection_state_t; typedef enum { COND_RESULT_UNSET, COND_RESULT_FALSE, COND_RESULT_TRUE } cond_result_t; @@ -311,95 +345,106 @@ typedef struct { int patterncount; int matches[3 * 10]; buffer *comp_value; /* just a pointer */ + + comp_key_t comp_type; } cond_cache_t; typedef struct { connection_state_t state; - + /* timestamps */ time_t read_idle_ts; time_t close_timeout_ts; time_t write_request_ts; - + time_t connection_start; time_t request_start; - + struct timeval start_tv; - + size_t request_count; /* number of requests handled in this connection */ size_t loops_per_request; /* to catch endless loops in a single request - * + * * used by mod_rewrite, mod_fastcgi, ... and others * this is self-protection */ - + int fd; /* the FD for this connection */ int fde_ndx; /* index for the fdevent-handler */ int ndx; /* reverse mapping to server->connection[ndx] */ - + /* fd states */ int is_readable; int is_writable; - - int keep_alive; /* only request.c can enable it, all other just disable */ - + + int keep_alive; /* only request.c can enable it, all other just disable */ + int keep_alive_idle; /* remember max_keep_alive_idle from config */ + int file_started; int file_finished; - + chunkqueue *write_queue; /* a large queue for low-level write ( HTTP response ) [ file, mem ] */ chunkqueue *read_queue; /* a small queue for low-level read ( HTTP request ) [ mem ] */ chunkqueue *request_content_queue; /* takes request-content into tempfile if necessary [ tempfile, mem ]*/ - + int traffic_limit_reached; - + off_t bytes_written; /* used by mod_accesslog, mod_rrd */ off_t bytes_written_cur_second; /* used by mod_accesslog, mod_rrd */ off_t bytes_read; /* used by mod_accesslog, mod_rrd */ off_t bytes_header; - + int http_status; - + sock_addr dst_addr; buffer *dst_addr_buf; /* request */ buffer *parse_request; unsigned int parsed_response; /* bitfield which contains the important header-fields of the parsed response header */ - + request request; request_uri uri; - physical physical; + physical physical; response response; - + size_t header_len; - + buffer *authed_user; array *environment; /* used to pass lighttpd internal stuff to the FastCGI/CGI apps, setenv does that */ - + /* response */ int got_response; - + int in_joblist; - + connection_type mode; - + void **plugin_ctx; /* plugin connection specific config */ - + specific_config conf; /* global connection specific config */ cond_cache_t *cond_cache; - + buffer *server_name; - + /* error-handler */ buffer *error_handler; int error_handler_saved_status; int in_error_handler; - + void *srv_socket; /* reference to the server-socket (typecast to server_socket) */ - + #ifdef USE_OPENSSL SSL *ssl; +# ifndef OPENSSL_NO_TLSEXT + buffer *tlsext_server_name; +# endif + unsigned int renegotiations; /* count of SSL_CB_HANDSHAKE_START */ #endif + /* etag handling */ + etag_flags_t etag_flags; + + int conditional_is_valid[COMP_LAST_ELEMENT]; } connection; typedef struct { @@ -442,90 +487,100 @@ typedef struct { typedef struct { unsigned short port; buffer *bindhost; - + buffer *errorlog_file; unsigned short errorlog_use_syslog; - + buffer *breakagelog_file; + unsigned short dont_daemonize; buffer *changeroot; buffer *username; buffer *groupname; - + buffer *pid_file; - + buffer *event_handler; - + buffer *modules_dir; buffer *network_backend; array *modules; array *upload_tempdirs; - + unsigned short max_worker; unsigned short max_fds; unsigned short max_conns; - unsigned short max_request_size; - + unsigned int max_request_size; + unsigned short log_request_header_on_error; unsigned short log_state_handling; - - enum { STAT_CACHE_ENGINE_UNSET, - STAT_CACHE_ENGINE_NONE, - STAT_CACHE_ENGINE_SIMPLE, - STAT_CACHE_ENGINE_FAM + + enum { STAT_CACHE_ENGINE_UNSET, + STAT_CACHE_ENGINE_NONE, + STAT_CACHE_ENGINE_SIMPLE +#ifdef HAVE_FAM_H + , STAT_CACHE_ENGINE_FAM +#endif } stat_cache_engine; unsigned short enable_cores; + unsigned short reject_expect_100_with_417; } server_config; typedef struct { sock_addr addr; int fd; int fde_ndx; - + buffer *ssl_pemfile; buffer *ssl_ca_file; + buffer *ssl_cipher_list; + buffer *ssl_dh_file; + buffer *ssl_ec_curve; + unsigned short ssl_use_sslv2; + unsigned short ssl_use_sslv3; unsigned short use_ipv6; unsigned short is_ssl; - + buffer *srv_token; - + #ifdef USE_OPENSSL SSL_CTX *ssl_ctx; #endif + unsigned short is_proxy_ssl; } server_socket; typedef struct { server_socket **ptr; - + size_t size; size_t used; } server_socket_array; typedef struct server { server_socket_array srv_sockets; - + /* the errorlog */ int errorlog_fd; - enum { ERRORLOG_STDERR, ERRORLOG_FILE, ERRORLOG_SYSLOG } errorlog_mode; + enum { ERRORLOG_FILE, ERRORLOG_FD, ERRORLOG_SYSLOG, ERRORLOG_PIPE } errorlog_mode; buffer *errorlog_buf; - + fdevents *ev, *ev_ins; - + buffer_plugin plugins; void *plugin_slots; - + /* counters */ int con_opened; int con_read; int con_written; int con_closed; - + int ssl_is_init; - + int max_fds; /* max possible fds */ int cur_fds; /* currently used fds */ int want_fds; /* waiting fds */ int sockets_disabled; - + size_t max_conns; /* buffers */ @@ -533,13 +588,13 @@ typedef struct server { buffer *response_header; buffer *response_range; buffer *tmp_buf; - + buffer *tmp_chunk_len; - + buffer *empty_string; /* is necessary for cond_match */ buffer *cond_check_buf; - + /* caches */ #ifdef HAVE_IPV6 inet_ntop_cache_type inet_ntop_cache[INET_NTOP_CACHE_MAX]; @@ -547,31 +602,35 @@ typedef struct server { mtime_cache_type mtime_cache[FILE_CACHE_MAX]; array *split_vals; - + /* Timestamps */ time_t cur_ts; time_t last_generated_date_ts; time_t last_generated_debug_ts; time_t startup_ts; - + + char entropy[8]; /* from /dev/[u]random if possible, otherwise rand() */ + char is_real_entropy; /* whether entropy is from /dev/[u]random */ + buffer *ts_debug_str; buffer *ts_date_str; - + /* config-file */ array *config; array *config_touched; - + array *config_context; specific_config **config_storage; - + server_config srvconf; - - int config_deprecated; - + + short int config_deprecated; + short int config_unsupported; + connections *conns; connections *joblist; connections *fdwaitqueue; - + stat_cache *stat_cache; /** @@ -588,14 +647,12 @@ typedef struct server { * fastcgi.backend.<key>.disconnects = ... */ array *status; - + fdevent_handler_t event_handler; - int (* network_backend_write)(struct server *srv, connection *con, int fd, chunkqueue *cq); - int (* network_backend_read)(struct server *srv, connection *con, int fd, chunkqueue *cq); + int (* network_backend_write)(struct server *srv, connection *con, int fd, chunkqueue *cq, off_t max_bytes); #ifdef USE_OPENSSL - int (* network_ssl_backend_write)(struct server *srv, connection *con, SSL *ssl, chunkqueue *cq); - int (* network_ssl_backend_read)(struct server *srv, connection *con, SSL *ssl, chunkqueue *cq); + int (* network_ssl_backend_write)(struct server *srv, connection *con, SSL *ssl, chunkqueue *cq, off_t max_bytes); #endif uid_t uid; diff --git a/src/bitset.c b/src/bitset.c index 7fe5662..555f244 100644 --- a/src/bitset.c +++ b/src/bitset.c @@ -1,12 +1,12 @@ +#include "buffer.h" +#include "bitset.h" + #include <limits.h> #include <stdlib.h> #include <string.h> #include <stdio.h> #include <assert.h> -#include "bitset.h" -#include "buffer.h" - #define BITSET_BITS \ ( CHAR_BIT * sizeof(size_t) ) diff --git a/src/buffer.c b/src/buffer.c index 40b8cb9..cff44fe 100644 --- a/src/buffer.c +++ b/src/buffer.c @@ -1,3 +1,5 @@ +#include "buffer.h" + #include <stdlib.h> #include <string.h> @@ -5,27 +7,25 @@ #include <assert.h> #include <ctype.h> -#include "buffer.h" - static const char hex_chars[] = "0123456789abcdef"; /** - * init the buffer - * + * init the buffer + * */ buffer* buffer_init(void) { buffer *b; - + b = malloc(sizeof(*b)); assert(b); - + b->ptr = NULL; b->size = 0; b->used = 0; - + return b; } @@ -36,8 +36,8 @@ buffer *buffer_init_buffer(buffer *src) { } /** - * free the buffer - * + * free the buffer + * */ void buffer_free(buffer *b) { @@ -49,39 +49,41 @@ void buffer_free(buffer *b) { void buffer_reset(buffer *b) { if (!b) return; - + /* limit don't reuse buffer larger than ... bytes */ if (b->size > BUFFER_MAX_REUSE_SIZE) { free(b->ptr); b->ptr = NULL; b->size = 0; + } else if (b->size) { + b->ptr[0] = '\0'; } - + b->used = 0; } /** - * - * allocate (if neccessary) enough space for 'size' bytes and + * + * allocate (if neccessary) enough space for 'size' bytes and * set the 'used' counter to 0 - * + * */ #define BUFFER_PIECE_SIZE 64 int buffer_prepare_copy(buffer *b, size_t size) { if (!b) return -1; - - if ((0 == b->size) || + + if ((0 == b->size) || (size > b->size)) { if (b->size) free(b->ptr); - + b->size = size; - + /* always allocate a multiply of BUFFER_PIECE_SIZE */ b->size += BUFFER_PIECE_SIZE - (b->size % BUFFER_PIECE_SIZE); - + b->ptr = malloc(b->size); assert(b->ptr); } @@ -90,30 +92,30 @@ int buffer_prepare_copy(buffer *b, size_t size) { } /** - * + * * increase the internal buffer (if neccessary) to append another 'size' byte * ->used isn't changed - * + * */ int buffer_prepare_append(buffer *b, size_t size) { if (!b) return -1; - + if (0 == b->size) { b->size = size; - + /* always allocate a multiply of BUFFER_PIECE_SIZE */ b->size += BUFFER_PIECE_SIZE - (b->size % BUFFER_PIECE_SIZE); - + b->ptr = malloc(b->size); b->used = 0; assert(b->ptr); } else if (b->used + size > b->size) { b->size += size; - + /* always allocate a multiply of BUFFER_PIECE_SIZE */ b->size += BUFFER_PIECE_SIZE - (b->size % BUFFER_PIECE_SIZE); - + b->ptr = realloc(b->ptr, b->size); assert(b->ptr); } @@ -122,7 +124,7 @@ int buffer_prepare_append(buffer *b, size_t size) { int buffer_copy_string(buffer *b, const char *s) { size_t s_len; - + if (!s || !b) return -1; s_len = strlen(s) + 1; @@ -136,28 +138,28 @@ int buffer_copy_string(buffer *b, const char *s) { int buffer_copy_string_len(buffer *b, const char *s, size_t s_len) { if (!s || !b) return -1; -#if 0 - /* removed optimization as we have to keep the empty string +#if 0 + /* removed optimization as we have to keep the empty string * in some cases for the config handling - * + * * url.access-deny = ( "" ) */ if (s_len == 0) return 0; -#endif +#endif buffer_prepare_copy(b, s_len + 1); - + memcpy(b->ptr, s, s_len); b->ptr[s_len] = '\0'; b->used = s_len + 1; - + return 0; } int buffer_copy_string_buffer(buffer *b, const buffer *src) { if (!src) return -1; - + if (src->used == 0) { - b->used = 0; + buffer_reset(b); return 0; } return buffer_copy_string_len(b, src->ptr, src->used - 1); @@ -185,6 +187,7 @@ int buffer_append_string_rfill(buffer *b, const char *s, size_t maxlen) { if (!s || !b) return -1; s_len = strlen(s); + if (s_len > maxlen) s_len = maxlen; buffer_prepare_append(b, maxlen + 1); if (b->used == 0) b->used++; @@ -201,10 +204,10 @@ int buffer_append_string_rfill(buffer *b, const char *s, size_t maxlen) { /** * append a string to the end of the buffer - * - * the resulting buffer is terminated with a '\0' + * + * the resulting buffer is terminated with a '\0' * s is treated as a un-terminated string (a \0 is handled a normal character) - * + * * @param b a buffer * @param s the string * @param s_len size of the string (without the terminating \0) @@ -228,7 +231,7 @@ int buffer_append_string_len(buffer *b, const char *s, size_t s_len) { int buffer_append_string_buffer(buffer *b, const buffer *src) { if (!src) return -1; if (src->used == 0) return 0; - + return buffer_append_string_len(b, src->ptr, src->used - 1); } @@ -245,9 +248,9 @@ int buffer_append_memory(buffer *b, const char *s, size_t s_len) { int buffer_copy_memory(buffer *b, const char *s, size_t s_len) { if (!s || !b) return -1; - + b->used = 0; - + return buffer_append_memory(b, s, s_len); } @@ -281,7 +284,7 @@ int buffer_append_long_hex(buffer *b, unsigned long value) { return 0; } -int ltostr(char *buf, long val) { +int LI_ltostr(char *buf, long val) { char swap; char *end; int len = 1; @@ -320,7 +323,7 @@ int buffer_append_long(buffer *b, long val) { if (b->used == 0) b->used++; - b->used += ltostr(b->ptr + (b->used - 1), val); + b->used += LI_ltostr(b->ptr + (b->used - 1), val); return 0; } @@ -402,46 +405,46 @@ char hex2int(unsigned char hex) { /** - * init the buffer - * + * init the buffer + * */ buffer_array* buffer_array_init(void) { buffer_array *b; - + b = malloc(sizeof(*b)); - + assert(b); b->ptr = NULL; b->size = 0; b->used = 0; - + return b; } void buffer_array_reset(buffer_array *b) { size_t i; - + if (!b) return; - + /* if they are too large, reduce them */ for (i = 0; i < b->used; i++) { buffer_reset(b->ptr[i]); } - + b->used = 0; } /** - * free the buffer_array - * + * free the buffer_array + * */ void buffer_array_free(buffer_array *b) { size_t i; if (!b) return; - + for (i = 0; i < b->size; i++) { if (b->ptr[i]) buffer_free(b->ptr[i]); } @@ -451,7 +454,7 @@ void buffer_array_free(buffer_array *b) { buffer *buffer_array_append_get_buffer(buffer_array *b) { size_t i; - + if (b->size == 0) { b->size = 16; b->ptr = malloc(sizeof(*b->ptr) * b->size); @@ -467,13 +470,13 @@ buffer *buffer_array_append_get_buffer(buffer_array *b) { b->ptr[i] = NULL; } } - + if (b->ptr[b->used] == NULL) { b->ptr[b->used] = buffer_init(); } - + b->ptr[b->used]->used = 0; - + return b->ptr[b->used++]; } @@ -482,33 +485,34 @@ char * buffer_search_string_len(buffer *b, const char *needle, size_t len) { size_t i; if (len == 0) return NULL; if (needle == NULL) return NULL; - + if (b->used < len) return NULL; - + for(i = 0; i < b->used - len; i++) { if (0 == memcmp(b->ptr + i, needle, len)) { return b->ptr + i; } } - + return NULL; } buffer *buffer_init_string(const char *str) { buffer *b = buffer_init(); - + buffer_copy_string(b, str); - + return b; } int buffer_is_empty(buffer *b) { + if (!b) return 1; return (b->used == 0); } /** * check if two buffer contain the same data - * + * * HISTORY: this function was pretty much optimized, but didn't handled * alignment properly. */ @@ -522,115 +526,121 @@ int buffer_is_equal(buffer *a, buffer *b) { int buffer_is_equal_string(buffer *a, const char *s, size_t b_len) { buffer b; - + b.ptr = (char *)s; b.used = b_len + 1; - + return buffer_is_equal(a, &b); } /* simple-assumption: - * + * * most parts are equal and doing a case conversion needs time - * + * */ int buffer_caseless_compare(const char *a, size_t a_len, const char *b, size_t b_len) { size_t ndx = 0, max_ndx; size_t *al, *bl; size_t mask = sizeof(*al) - 1; - + al = (size_t *)a; bl = (size_t *)b; - + /* is the alignment correct ? */ if ( ((size_t)al & mask) == 0 && ((size_t)bl & mask) == 0 ) { - + max_ndx = ((a_len < b_len) ? a_len : b_len) & ~mask; - + for (; ndx < max_ndx; ndx += sizeof(*al)) { if (*al != *bl) break; al++; bl++; - + } - + } - + a = (char *)al; b = (char *)bl; - + max_ndx = ((a_len < b_len) ? a_len : b_len); - + for (; ndx < max_ndx; ndx++) { - char a1 = *a++, b1 = *b++; - + int a1 = *a++, b1 = *b++; + if (a1 != b1) { - if ((a1 >= 'A' && a1 <= 'Z') && (b1 >= 'a' && b1 <= 'z')) - a1 |= 32; - else if ((a1 >= 'a' && a1 <= 'z') && (b1 >= 'A' && b1 <= 'Z')) - b1 |= 32; + /* always lowercase for transitive results */ + if (a1 >= 'A' && a1 <= 'Z') a1 |= 32; + if (b1 >= 'A' && b1 <= 'Z') b1 |= 32; + if ((a1 - b1) != 0) return (a1 - b1); - } } - - return 0; + + /* all chars are the same, and the length match too + * + * they are the same */ + if (a_len == b_len) return 0; + + /* if a is shorter then b, then b is larger */ + return (a_len - b_len); } /** * check if the rightmost bytes of the string are equal. - * - * + * + * */ int buffer_is_equal_right_len(buffer *b1, buffer *b2, size_t len) { /* no, len -> equal */ if (len == 0) return 1; - + /* len > 0, but empty buffers -> not equal */ if (b1->used == 0 || b2->used == 0) return 0; - + /* buffers too small -> not equal */ if (b1->used - 1 < len || b1->used - 1 < len) return 0; - - if (0 == strncmp(b1->ptr + b1->used - 1 - len, + + if (0 == strncmp(b1->ptr + b1->used - 1 - len, b2->ptr + b2->used - 1 - len, len)) { return 1; } - + return 0; } int buffer_copy_string_hex(buffer *b, const char *in, size_t in_len) { size_t i; - + /* BO protection */ if (in_len * 2 < in_len) return -1; - + buffer_prepare_copy(b, in_len * 2 + 1); - + for (i = 0; i < in_len; i++) { b->ptr[b->used++] = hex_chars[(in[i] >> 4) & 0x0F]; b->ptr[b->used++] = hex_chars[in[i] & 0x0F]; } b->ptr[b->used++] = '\0'; - + return 0; } -const char encoded_chars_rel_uri_part[] = { +/* everything except: ! ( ) * - . 0-9 A-Z _ a-z */ +static const char encoded_chars_rel_uri_part[] = { /* 0 1 2 3 4 5 6 7 8 9 A B C D E F */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 00 - 0F control chars */ - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 10 - 1F */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 10 - 1F */ 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, /* 20 - 2F space " # $ % & ' + , / */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, /* 30 - 3F : ; = ? @ < > */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 40 - 4F */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 50 - 5F */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 60 - 6F */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, /* 70 - 7F DEL */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, /* 30 - 3F : ; < = > ? */ + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 40 - 4F @ */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, /* 50 - 5F [ \ ] ^ */ + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 60 - 6F ` */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, /* 70 - 7F { | } ~ DEL */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 80 - 8F */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 90 - 9F */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* A0 - AF */ @@ -641,18 +651,19 @@ const char encoded_chars_rel_uri_part[] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* F0 - FF */ }; -const char encoded_chars_rel_uri[] = { +/* everything except: ! ( ) * - . / 0-9 A-Z _ a-z */ +static const char encoded_chars_rel_uri[] = { /* 0 1 2 3 4 5 6 7 8 9 A B C D E F */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 00 - 0F control chars */ - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 10 - 1F */ - 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, /* 20 - 2F space " # $ % & ' + , / */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, /* 30 - 3F : ; = ? @ < > */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 40 - 4F */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 50 - 5F */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 60 - 6F */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, /* 70 - 7F DEL */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 10 - 1F */ + 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, /* 20 - 2F space " # $ % & ' + , */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, /* 30 - 3F : ; < = > ? */ + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 40 - 4F @ */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, /* 50 - 5F [ \ ] ^ */ + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 60 - 6F ` */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, /* 70 - 7F { | } ~ DEL */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 80 - 8F */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 90 - 9F */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* A0 - AF */ @@ -663,12 +674,12 @@ const char encoded_chars_rel_uri[] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* F0 - FF */ }; -const char encoded_chars_html[] = { +static const char encoded_chars_html[] = { /* 0 1 2 3 4 5 6 7 8 9 A B C D E F */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 00 - 0F control chars */ - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 10 - 1F */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 10 - 1F */ 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 20 - 2F & */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, /* 30 - 3F < > */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 40 - 4F */ @@ -685,12 +696,12 @@ const char encoded_chars_html[] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* F0 - FF */ }; -const char encoded_chars_minimal_xml[] = { +static const char encoded_chars_minimal_xml[] = { /* 0 1 2 3 4 5 6 7 8 9 A B C D E F */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 00 - 0F control chars */ - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 10 - 1F */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 10 - 1F */ 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 20 - 2F & */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, /* 30 - 3F < > */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 40 - 4F */ @@ -707,17 +718,17 @@ const char encoded_chars_minimal_xml[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* F0 - FF */ }; -const char encoded_chars_hex[] = { +static const char encoded_chars_hex[] = { /* 0 1 2 3 4 5 6 7 8 9 A B C D E F */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 00 - 0F control chars */ - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 10 - 1F */ - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 20 - 2F */ - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 30 - 3F */ - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 40 - 4F */ - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 50 - 5F */ - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 60 - 6F */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 10 - 1F */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 20 - 2F */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 30 - 3F */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 40 - 4F */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 50 - 5F */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 60 - 6F */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 70 - 7F */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 80 - 8F */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 90 - 9F */ @@ -729,18 +740,41 @@ const char encoded_chars_hex[] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* F0 - FF */ }; +static const char encoded_chars_http_header[] = { + /* + 0 1 2 3 4 5 6 7 8 9 A B C D E F + */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, /* 00 - 0F */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 10 - 1F */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 20 - 2F */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 30 - 3F */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 40 - 4F */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 50 - 5F */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 60 - 6F */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 70 - 7F */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 80 - 8F */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 90 - 9F */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* A0 - AF */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* B0 - BF */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* C0 - CF */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* D0 - DF */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* E0 - EF */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* F0 - FF */ +}; + + int buffer_append_string_encoded(buffer *b, const char *s, size_t s_len, buffer_encoding_t encoding) { unsigned char *ds, *d; size_t d_len, ndx; const char *map = NULL; - + if (!s || !b) return -1; - + if (b->ptr[b->used - 1] != '\0') { SEGFAULT(); } - + if (s_len == 0) return 0; switch(encoding) { @@ -759,12 +793,15 @@ int buffer_append_string_encoded(buffer *b, const char *s, size_t s_len, buffer_ case ENCODING_HEX: map = encoded_chars_hex; break; + case ENCODING_HTTP_HEADER: + map = encoded_chars_http_header; + break; case ENCODING_UNSET: break; } assert(map != NULL); - + /* count to-be-encoded-characters */ for (ds = (unsigned char *)s, d_len = 0, ndx = 0; ndx < s_len; ds++, ndx++) { if (map[*ds]) { @@ -777,6 +814,7 @@ int buffer_append_string_encoded(buffer *b, const char *s, size_t s_len, buffer_ case ENCODING_MINIMAL_XML: d_len += 6; break; + case ENCODING_HTTP_HEADER: case ENCODING_HEX: d_len += 2; break; @@ -787,9 +825,9 @@ int buffer_append_string_encoded(buffer *b, const char *s, size_t s_len, buffer_ d_len ++; } } - + buffer_prepare_append(b, d_len); - + for (ds = (unsigned char *)s, d = (unsigned char *)b->ptr + b->used - 1, d_len = 0, ndx = 0; ndx < s_len; ds++, ndx++) { if (map[*ds]) { switch(encoding) { @@ -812,6 +850,10 @@ int buffer_append_string_encoded(buffer *b, const char *s, size_t s_len, buffer_ d[d_len++] = hex_chars[((*ds) >> 4) & 0x0F]; d[d_len++] = hex_chars[(*ds) & 0x0F]; break; + case ENCODING_HTTP_HEADER: + d[d_len++] = *ds; + d[d_len++] = '\t'; + break; case ENCODING_UNSET: break; } @@ -820,9 +862,9 @@ int buffer_append_string_encoded(buffer *b, const char *s, size_t s_len, buffer_ } } - /* terminate buffer and calculate new length */ + /* terminate buffer and calculate new length */ b->ptr[b->used + d_len - 1] = '\0'; - + b->used += d_len; return 0; @@ -854,10 +896,10 @@ static int buffer_urldecode_internal(buffer *url, int is_query) { low = hex2int(*(src + 2)); if (low != 0xFF) { high = (high << 4) | low; - - /* map control-characters out */ + + /* map control-characters out */ if (high < 32 || high == 127) high = '_'; - + *dst = high; src += 2; } @@ -914,6 +956,18 @@ int buffer_path_simplify(buffer *dest, buffer *src) start = dest->ptr; out = dest->ptr; slash = dest->ptr; + + +#if defined(__WIN32) || defined(__CYGWIN__) + /* cygwin is treating \ and / the same, so we have to that too + */ + + for (walk = src->ptr; *walk; walk++) { + if (*walk == '\\') *walk = '/'; + } + walk = src->ptr; +#endif + while (*walk == ' ') { walk++; } @@ -979,7 +1033,7 @@ int light_isdigit(int c) { int light_isxdigit(int c) { if (light_isdigit(c)) return 1; - + c |= 32; return (c >= 'a' && c <= 'f'); } @@ -995,29 +1049,29 @@ int light_isalnum(int c) { int buffer_to_lower(buffer *b) { char *c; - + if (b->used == 0) return 0; - + for (c = b->ptr; *c; c++) { if (*c >= 'A' && *c <= 'Z') { *c |= 32; } } - + return 0; } int buffer_to_upper(buffer *b) { char *c; - + if (b->used == 0) return 0; - + for (c = b->ptr; *c; c++) { if (*c >= 'a' && *c <= 'z') { *c &= ~32; } } - + return 0; } diff --git a/src/buffer.h b/src/buffer.h index 3ca22e5..bda0424 100644 --- a/src/buffer.h +++ b/src/buffer.h @@ -1,34 +1,35 @@ #ifndef _BUFFER_H_ #define _BUFFER_H_ -#include <stdlib.h> -#include <sys/types.h> - #ifdef HAVE_CONFIG_H # include "config.h" #endif #include "settings.h" +#include <stdlib.h> +#include <sys/types.h> +#include <stdio.h> + typedef struct { char *ptr; - + size_t used; size_t size; } buffer; typedef struct { buffer **ptr; - + size_t used; size_t size; } buffer_array; typedef struct { char *ptr; - + size_t offset; /* input-pointer */ - + size_t used; /* output-pointer */ size_t size; } read_buffer; @@ -43,7 +44,7 @@ buffer* buffer_init_buffer(buffer *b); buffer* buffer_init_string(const char *str); void buffer_free(buffer *b); void buffer_reset(buffer *b); - + int buffer_prepare_copy(buffer *b, size_t size); int buffer_prepare_append(buffer *b, size_t size); @@ -87,9 +88,10 @@ typedef enum { ENCODING_UNSET, ENCODING_REL_URI, /* for coding a rel-uri (/with space/and%percent) nicely as part of a href */ ENCODING_REL_URI_PART, /* same as ENC_REL_URL plus coding / too as %2F */ - ENCODING_HTML, /* & becomes & and so on */ - ENCODING_MINIMAL_XML, /* minimal encoding for xml */ - ENCODING_HEX /* encode string as hex */ + ENCODING_HTML, /* & becomes & and so on */ + ENCODING_MINIMAL_XML, /* minimal encoding for xml */ + ENCODING_HEX, /* encode string as hex */ + ENCODING_HTTP_HEADER /* encode \n with \t\n */ } buffer_encoding_t; int buffer_append_string_encoded(buffer *b, const char *s, size_t s_len, buffer_encoding_t encoding); @@ -102,7 +104,7 @@ int buffer_to_lower(buffer *b); int buffer_to_upper(buffer *b); /** deprecated */ -int ltostr(char *buf, long val); +int LI_ltostr(char *buf, long val); char hex2int(unsigned char c); char int2hex(char i); diff --git a/src/chunk.c b/src/chunk.c index 3903428..7583db6 100644 --- a/src/chunk.c +++ b/src/chunk.c @@ -1,9 +1,11 @@ /** * the network chunk-API - * - * + * + * */ +#include "chunk.h" + #include <sys/types.h> #include <sys/stat.h> #include <sys/mman.h> @@ -16,38 +18,36 @@ #include <errno.h> #include <string.h> -#include "chunk.h" - chunkqueue *chunkqueue_init(void) { chunkqueue *cq; - + cq = calloc(1, sizeof(*cq)); - + cq->first = NULL; cq->last = NULL; - + cq->unused = NULL; - + return cq; } static chunk *chunk_init(void) { chunk *c; - + c = calloc(1, sizeof(*c)); - + c->mem = buffer_init(); c->file.name = buffer_init(); c->file.fd = -1; c->file.mmap.start = MAP_FAILED; c->next = NULL; - + return c; } static void chunk_free(chunk *c) { if (!c) return; - + buffer_free(c->mem); buffer_free(c->file.name); @@ -56,13 +56,13 @@ static void chunk_free(chunk *c) { static void chunk_reset(chunk *c) { if (!c) return; - + buffer_reset(c->mem); if (c->file.is_temp && !buffer_is_empty(c->file.name)) { unlink(c->file.name->ptr); } - + buffer_reset(c->file.name); if (c->file.fd != -1) { @@ -78,27 +78,27 @@ static void chunk_reset(chunk *c) { void chunkqueue_free(chunkqueue *cq) { chunk *c, *pc; - + if (!cq) return; - + for (c = cq->first; c; ) { pc = c; c = c->next; chunk_free(pc); } - + for (c = cq->unused; c; ) { pc = c; c = c->next; chunk_free(pc); } - + free(cq); } static chunk *chunkqueue_get_unused_chunk(chunkqueue *cq) { chunk *c; - + /* check if we have a unused chunk */ if (!cq->unused) { c = chunk_init(); @@ -109,18 +109,18 @@ static chunk *chunkqueue_get_unused_chunk(chunkqueue *cq) { c->next = NULL; cq->unused_chunks--; } - + return c; } static int chunkqueue_prepend_chunk(chunkqueue *cq, chunk *c) { c->next = cq->first; cq->first = c; - + if (cq->last == NULL) { cq->last = c; } - + return 0; } @@ -129,19 +129,19 @@ static int chunkqueue_append_chunk(chunkqueue *cq, chunk *c) { cq->last->next = c; } cq->last = c; - + if (cq->first == NULL) { cq->first = c; } - + return 0; } void chunkqueue_reset(chunkqueue *cq) { chunk *c; /* move everything to the unused queue */ - - /* mark all read written */ + + /* mark all read written */ for (c = cq->first; c; c = c->next) { switch(c->type) { case MEM_CHUNK: @@ -150,7 +150,7 @@ void chunkqueue_reset(chunkqueue *cq) { case FILE_CHUNK: c->offset = c->file.length; break; - default: + default: break; } } @@ -162,93 +162,108 @@ void chunkqueue_reset(chunkqueue *cq) { int chunkqueue_append_file(chunkqueue *cq, buffer *fn, off_t offset, off_t len) { chunk *c; - + if (len == 0) return 0; - + c = chunkqueue_get_unused_chunk(cq); - + c->type = FILE_CHUNK; - + buffer_copy_string_buffer(c->file.name, fn); c->file.start = offset; c->file.length = len; c->offset = 0; - + chunkqueue_append_chunk(cq, c); - + return 0; } int chunkqueue_append_buffer(chunkqueue *cq, buffer *mem) { chunk *c; - + if (mem->used == 0) return 0; - + c = chunkqueue_get_unused_chunk(cq); c->type = MEM_CHUNK; c->offset = 0; buffer_copy_string_buffer(c->mem, mem); - + + chunkqueue_append_chunk(cq, c); + + return 0; +} + +int chunkqueue_append_buffer_weak(chunkqueue *cq, buffer *mem) { + chunk *c; + + c = chunkqueue_get_unused_chunk(cq); + c->type = MEM_CHUNK; + c->offset = 0; + if (c->mem) buffer_free(c->mem); + c->mem = mem; + chunkqueue_append_chunk(cq, c); - + return 0; } int chunkqueue_prepend_buffer(chunkqueue *cq, buffer *mem) { chunk *c; - + if (mem->used == 0) return 0; - + c = chunkqueue_get_unused_chunk(cq); c->type = MEM_CHUNK; c->offset = 0; buffer_copy_string_buffer(c->mem, mem); - + chunkqueue_prepend_chunk(cq, c); - + return 0; } + int chunkqueue_append_mem(chunkqueue *cq, const char * mem, size_t len) { chunk *c; - + if (len == 0) return 0; - + c = chunkqueue_get_unused_chunk(cq); c->type = MEM_CHUNK; c->offset = 0; buffer_copy_string_len(c->mem, mem, len - 1); - + chunkqueue_append_chunk(cq, c); - + return 0; } buffer * chunkqueue_get_prepend_buffer(chunkqueue *cq) { chunk *c; - + c = chunkqueue_get_unused_chunk(cq); - + c->type = MEM_CHUNK; c->offset = 0; buffer_reset(c->mem); - + chunkqueue_prepend_chunk(cq, c); - + return c->mem; } buffer *chunkqueue_get_append_buffer(chunkqueue *cq) { chunk *c; - + c = chunkqueue_get_unused_chunk(cq); - + c->type = MEM_CHUNK; c->offset = 0; buffer_reset(c->mem); - + chunkqueue_append_chunk(cq, c); - + return c->mem; } @@ -263,7 +278,7 @@ int chunkqueue_set_tempdirs(chunkqueue *cq, array *tempdirs) { chunk *chunkqueue_get_append_tempfile(chunkqueue *cq) { chunk *c; buffer *template = buffer_init_string("/var/tmp/lighttpd-upload-XXXXXX"); - + c = chunkqueue_get_unused_chunk(cq); c->type = FILE_CHUNK; @@ -273,13 +288,13 @@ chunk *chunkqueue_get_append_tempfile(chunkqueue *cq) { size_t i; /* we have several tempdirs, only if all of them fail we jump out */ - + for (i = 0; i < cq->tempdirs->used; i++) { data_string *ds = (data_string *)cq->tempdirs->data[i]; buffer_copy_string_buffer(template, ds->value); BUFFER_APPEND_SLASH(template); - BUFFER_APPEND_STRING_CONST(template, "lighttpd-upload-XXXXXX"); + buffer_append_string_len(template, CONST_STR_LEN("lighttpd-upload-XXXXXX")); if (-1 != (c->file.fd = mkstemp(template->ptr))) { /* only trigger the unlink if we created the temp-file successfully */ @@ -300,7 +315,7 @@ chunk *chunkqueue_get_append_tempfile(chunkqueue *cq) { chunkqueue_append_chunk(cq, c); buffer_free(template); - + return c; } @@ -308,7 +323,7 @@ chunk *chunkqueue_get_append_tempfile(chunkqueue *cq) { off_t chunkqueue_length(chunkqueue *cq) { off_t len = 0; chunk *c; - + for (c = cq->first; c; c = c->next) { switch (c->type) { case MEM_CHUNK: @@ -321,14 +336,14 @@ off_t chunkqueue_length(chunkqueue *cq) { break; } } - + return len; } off_t chunkqueue_written(chunkqueue *cq) { off_t len = 0; chunk *c; - + for (c = cq->first; c; c = c->next) { switch (c->type) { case MEM_CHUNK: @@ -339,7 +354,7 @@ off_t chunkqueue_written(chunkqueue *cq) { break; } } - + return len; } @@ -355,12 +370,12 @@ int chunkqueue_remove_finished_chunks(chunkqueue *cq) { switch (c->type) { case MEM_CHUNK: - if (c->offset == (off_t)c->mem->used - 1) is_finished = 1; + if (c->mem->used == 0 || (c->offset == (off_t)c->mem->used - 1)) is_finished = 1; break; case FILE_CHUNK: - if (c->offset == c->file.length) is_finished = 1; + if (c->offset == c->file.length) is_finished = 1; break; - default: + default: break; } diff --git a/src/chunk.h b/src/chunk.h index ddc8617..e43d3eb 100644 --- a/src/chunk.h +++ b/src/chunk.h @@ -3,10 +3,11 @@ #include "buffer.h" #include "array.h" +#include "sys-mmap.h" typedef struct chunk { enum { UNUSED_CHUNK, MEM_CHUNK, FILE_CHUNK } type; - + buffer *mem; /* either the storage of the mem-chunk or the read-ahead buffer */ struct { @@ -16,7 +17,7 @@ typedef struct chunk { off_t length; /* octets to send from the starting offset */ int fd; - struct { + struct { char *start; /* the start pointer of the mmap'ed area */ size_t length; /* size of the mmap'ed area */ off_t offset; /* start is <n> octet away from the start of the file */ @@ -24,20 +25,20 @@ typedef struct chunk { int is_temp; /* file is temporary and will be deleted if on cleanup */ } file; - - off_t offset; /* octets sent from this chunk - the size of the chunk is either + + off_t offset; /* octets sent from this chunk + the size of the chunk is either - mem-chunk: mem->used - 1 - file-chunk: file.length */ - + struct chunk *next; } chunk; typedef struct { chunk *first; chunk *last; - + chunk *unused; size_t unused_chunks; @@ -51,6 +52,7 @@ int chunkqueue_set_tempdirs(chunkqueue *c, array *tempdirs); int chunkqueue_append_file(chunkqueue *c, buffer *fn, off_t offset, off_t len); int chunkqueue_append_mem(chunkqueue *c, const char *mem, size_t len); int chunkqueue_append_buffer(chunkqueue *c, buffer *mem); +int chunkqueue_append_buffer_weak(chunkqueue *c, buffer *mem); int chunkqueue_prepend_buffer(chunkqueue *c, buffer *mem); buffer * chunkqueue_get_append_buffer(chunkqueue *c); diff --git a/src/configfile-glue.c b/src/configfile-glue.c index 5c3c68b..3efa46a 100644 --- a/src/configfile-glue.c +++ b/src/configfile-glue.c @@ -1,20 +1,23 @@ -#include <string.h> - #include "base.h" #include "buffer.h" #include "array.h" #include "log.h" #include "plugin.h" +#include "configfile.h" + +#include <string.h> +#include <stdlib.h> + /** * like all glue code this file contains functions which * are the external interface of lighttpd. The functions * are used by the server itself and the plugins. * - * The main-goal is to have a small library in the end - * which is linked against both and which will define + * The main-goal is to have a small library in the end + * which is linked against both and which will define * the interface itself in the end. - * + * */ @@ -24,56 +27,56 @@ int config_insert_values_internal(server *srv, array *ca, const config_values_t cv[]) { size_t i; data_unset *du; - + for (i = 0; cv[i].key; i++) { - + if (NULL == (du = array_get_element(ca, cv[i].key))) { /* no found */ - + continue; } - + switch (cv[i].type) { case T_CONFIG_ARRAY: if (du->type == TYPE_ARRAY) { size_t j; data_array *da = (data_array *)du; - + for (j = 0; j < da->value->used; j++) { if (da->value->data[j]->type == TYPE_STRING) { data_string *ds = data_string_init(); - + buffer_copy_string_buffer(ds->value, ((data_string *)(da->value->data[j]))->value); if (!da->is_index_key) { /* the id's were generated automaticly, as we copy now we might have to renumber them - * this is used to prepend server.modules by mod_indexfiles as it has to be loaded + * this is used to prepend server.modules by mod_indexfile as it has to be loaded * before mod_fastcgi and friends */ buffer_copy_string_buffer(ds->key, ((data_string *)(da->value->data[j]))->key); } - + array_insert_unique(cv[i].destination, (data_unset *)ds); } else { - log_error_write(srv, __FILE__, __LINE__, "sssd", - "the key of and array can only be a string or a integer, variable:", - cv[i].key, "type:", da->value->data[j]->type); - + log_error_write(srv, __FILE__, __LINE__, "sssd", + "the key of an array can only be a string or a integer, variable:", + cv[i].key, "type:", da->value->data[j]->type); + return -1; } } } else { - log_error_write(srv, __FILE__, __LINE__, "sss", "unexpected type for key: ", cv[i].key, "array of strings"); - + log_error_write(srv, __FILE__, __LINE__, "ss", cv[i].key, "should have been a array of strings like ... = ( \"...\" )"); + return -1; } break; case T_CONFIG_STRING: if (du->type == TYPE_STRING) { data_string *ds = (data_string *)du; - + buffer_copy_string_buffer(cv[i].destination, ds->value); } else { - log_error_write(srv, __FILE__, __LINE__, "ssss", "unexpected type for key: ", cv[i].key, "(string)", "\"...\""); - + log_error_write(srv, __FILE__, __LINE__, "ssss", cv[i].key, "should have been a string like ... = \"...\""); + return -1; } break; @@ -81,81 +84,133 @@ int config_insert_values_internal(server *srv, array *ca, const config_values_t switch(du->type) { case TYPE_INTEGER: { data_integer *di = (data_integer *)du; - + *((unsigned short *)(cv[i].destination)) = di->value; break; } case TYPE_STRING: { data_string *ds = (data_string *)du; - - log_error_write(srv, __FILE__, __LINE__, "ssb", "get a string but expected a short:", cv[i].key, ds->value); - + + /* If the value came from an environment variable, then it is a + * data_string, although it may contain a number in ASCII + * decimal format. We try to interpret the string as a decimal + * short before giving up, in order to support setting numeric + * values with environment variables (eg, port number). + */ + if (ds->value->ptr && *ds->value->ptr) { + char *e; + long l = strtol(ds->value->ptr, &e, 10); + if (e != ds->value->ptr && !*e && l >=0 && l <= 65535) { + *((unsigned short *)(cv[i].destination)) = l; + break; + } + } + + log_error_write(srv, __FILE__, __LINE__, "ssb", "got a string but expected a short:", cv[i].key, ds->value); + return -1; } default: - log_error_write(srv, __FILE__, __LINE__, "ssds", "unexpected type for key:", cv[i].key, du->type, "expected a integer, range 0 ... 65535"); + log_error_write(srv, __FILE__, __LINE__, "ssds", "unexpected type for key:", cv[i].key, du->type, "expected a short integer, range 0 ... 65535"); + return -1; + } + break; + case T_CONFIG_INT: + switch(du->type) { + case TYPE_INTEGER: { + data_integer *di = (data_integer *)du; + + *((unsigned int *)(cv[i].destination)) = di->value; + break; + } + case TYPE_STRING: { + data_string *ds = (data_string *)du; + + if (ds->value->ptr && *ds->value->ptr) { + char *e; + long l = strtol(ds->value->ptr, &e, 10); + if (e != ds->value->ptr && !*e && l >= 0) { + *((unsigned int *)(cv[i].destination)) = l; + break; + } + } + + + log_error_write(srv, __FILE__, __LINE__, "ssb", "got a string but expected an integer:", cv[i].key, ds->value); + + return -1; + } + default: + log_error_write(srv, __FILE__, __LINE__, "ssds", "unexpected type for key:", cv[i].key, du->type, "expected an integer, range 0 ... 4294967295"); return -1; } break; case T_CONFIG_BOOLEAN: if (du->type == TYPE_STRING) { data_string *ds = (data_string *)du; - + if (buffer_is_equal_string(ds->value, CONST_STR_LEN("enable"))) { *((unsigned short *)(cv[i].destination)) = 1; } else if (buffer_is_equal_string(ds->value, CONST_STR_LEN("disable"))) { *((unsigned short *)(cv[i].destination)) = 0; } else { log_error_write(srv, __FILE__, __LINE__, "ssbs", "ERROR: unexpected value for key:", cv[i].key, ds->value, "(enable|disable)"); - + return -1; } } else { log_error_write(srv, __FILE__, __LINE__, "ssss", "ERROR: unexpected type for key:", cv[i].key, "(string)", "\"(enable|disable)\""); - + return -1; } break; case T_CONFIG_LOCAL: case T_CONFIG_UNSET: break; + case T_CONFIG_UNSUPPORTED: + log_error_write(srv, __FILE__, __LINE__, "ssss", "ERROR: found unsupported key:", cv[i].key, "-", (char *)(cv[i].destination)); + + srv->config_unsupported = 1; + + break; case T_CONFIG_DEPRECATED: log_error_write(srv, __FILE__, __LINE__, "ssss", "ERROR: found deprecated key:", cv[i].key, "-", (char *)(cv[i].destination)); - + srv->config_deprecated = 1; - + break; } } + return 0; } int config_insert_values_global(server *srv, array *ca, const config_values_t cv[]) { size_t i; data_unset *du; - + for (i = 0; cv[i].key; i++) { data_string *touched; - + if (NULL == (du = array_get_element(ca, cv[i].key))) { /* no found */ - + continue; } - + /* touched */ touched = data_string_init(); - - buffer_copy_string(touched->value, ""); + + buffer_copy_string_len(touched->value, CONST_STR_LEN("")); buffer_copy_string_buffer(touched->key, du->key); - + array_insert_unique(srv->config_touched, (data_unset *)touched); } - + return config_insert_values_internal(srv, ca, cv); } -unsigned short sock_addr_get_port(sock_addr *addr) { +static unsigned short sock_addr_get_port(sock_addr *addr) { #ifdef HAVE_IPV6 return ntohs(addr->plain.sa_family ? addr->ipv6.sin6_port : addr->ipv4.sin_port); #else @@ -168,59 +223,89 @@ static cond_result_t config_check_cond_cached(server *srv, connection *con, data static cond_result_t config_check_cond_nocache(server *srv, connection *con, data_config *dc) { buffer *l; server_socket *srv_sock = con->srv_socket; + /* check parent first */ if (dc->parent && dc->parent->context_ndx) { + /** + * a nested conditional + * + * if the parent is not decided yet or false, we can't be true either + */ if (con->conf.log_condition_handling) { log_error_write(srv, __FILE__, __LINE__, "sb", "go parent", dc->parent->key); } - if (config_check_cond_cached(srv, con, dc->parent) == COND_RESULT_FALSE) { + + switch (config_check_cond_cached(srv, con, dc->parent)) { + case COND_RESULT_FALSE: return COND_RESULT_FALSE; + case COND_RESULT_UNSET: + return COND_RESULT_UNSET; + default: + break; } } if (dc->prev) { + /** + * a else branch + * + * we can only be executed, if all of our previous brothers + * are false + */ if (con->conf.log_condition_handling) { log_error_write(srv, __FILE__, __LINE__, "sb", "go prev", dc->prev->key); } + /* make sure prev is checked first */ config_check_cond_cached(srv, con, dc->prev); + /* one of prev set me to FALSE */ - if (COND_RESULT_FALSE == con->cond_cache[dc->context_ndx].result) { - return COND_RESULT_FALSE; + switch (con->cond_cache[dc->context_ndx].result) { + case COND_RESULT_FALSE: + return con->cond_cache[dc->context_ndx].result; + default: + break; } } + if (!con->conditional_is_valid[dc->comp]) { + if (con->conf.log_condition_handling) { + log_error_write(srv, __FILE__, __LINE__, "dss", + dc->comp, + dc->key->ptr, + con->conditional_is_valid[dc->comp] ? "yeah" : "nej"); + } + + return COND_RESULT_UNSET; + } + /* pass the rules */ - + switch (dc->comp) { case COMP_HTTP_HOST: { char *ck_colon = NULL, *val_colon = NULL; - + if (!buffer_is_empty(con->uri.authority)) { - - /* + + /* * append server-port to the HTTP_POST if necessary */ - + l = con->uri.authority; - + switch(dc->cond) { case CONFIG_COND_NE: case CONFIG_COND_EQ: ck_colon = strchr(dc->string->ptr, ':'); val_colon = strchr(l->ptr, ':'); - - if (ck_colon == val_colon) { - /* nothing to do with it */ - break; - } - if (ck_colon) { + + if (NULL != ck_colon && NULL == val_colon) { /* condition "host:port" but client send "host" */ buffer_copy_string_buffer(srv->cond_check_buf, l); - BUFFER_APPEND_STRING_CONST(srv->cond_check_buf, ":"); + buffer_append_string_len(srv->cond_check_buf, CONST_STR_LEN(":")); buffer_append_long(srv->cond_check_buf, sock_addr_get_port(&(srv_sock->addr))); l = srv->cond_check_buf; - } else if (!ck_colon) { + } else if (NULL != val_colon && NULL == ck_colon) { /* condition "host" but client send "host:port" */ buffer_copy_string_len(srv->cond_check_buf, l->ptr, val_colon - l->ptr); l = srv->cond_check_buf; @@ -229,22 +314,26 @@ static cond_result_t config_check_cond_nocache(server *srv, connection *con, dat default: break; } +#if defined USE_OPENSSL && ! defined OPENSSL_NO_TLSEXT + } else if (!buffer_is_empty(con->tlsext_server_name)) { + l = con->tlsext_server_name; +#endif } else { - l = NULL; + l = srv->empty_string; } break; } - case COMP_HTTP_REMOTEIP: { + case COMP_HTTP_REMOTE_IP: { char *nm_slash; - /* handle remoteip limitations - * + /* handle remoteip limitations + * * "10.0.0.1" is provided for all comparisions - * + * * only for == and != we support - * + * * "10.0.0.1/24" */ - + if ((dc->cond == CONFIG_COND_EQ || dc->cond == CONFIG_COND_NE) && (con->dst_addr.plain.sa_family == AF_INET) && @@ -253,41 +342,41 @@ static cond_result_t config_check_cond_nocache(server *srv, connection *con, dat long nm; char *err; struct in_addr val_inp; - + if (*(nm_slash+1) == '\0') { log_error_write(srv, __FILE__, __LINE__, "sb", "ERROR: no number after / ", dc->string); - + return COND_RESULT_FALSE; } - + nm_bits = strtol(nm_slash + 1, &err, 10); - + if (*err) { - log_error_write(srv, __FILE__, __LINE__, "sbs", "ERROR: non-digit found in netmask:", dc->string, *err); - + log_error_write(srv, __FILE__, __LINE__, "sbs", "ERROR: non-digit found in netmask:", dc->string, err); + return COND_RESULT_FALSE; } - + /* take IP convert to the native */ buffer_copy_string_len(srv->cond_check_buf, dc->string->ptr, nm_slash - dc->string->ptr); -#ifdef __WIN32 +#ifdef __WIN32 if (INADDR_NONE == (val_inp.s_addr = inet_addr(srv->cond_check_buf->ptr))) { log_error_write(srv, __FILE__, __LINE__, "sb", "ERROR: ip addr is invalid:", srv->cond_check_buf); - + return COND_RESULT_FALSE; } #else if (0 == inet_aton(srv->cond_check_buf->ptr, &val_inp)) { log_error_write(srv, __FILE__, __LINE__, "sb", "ERROR: ip addr is invalid:", srv->cond_check_buf); - + return COND_RESULT_FALSE; } #endif - + /* build netmask */ nm = htonl(~((1 << (32 - nm_bits)) - 1)); - + if ((val_inp.s_addr & nm) == (con->dst_addr.ipv4.sin_addr.s_addr & nm)) { return (dc->cond == CONFIG_COND_EQ) ? COND_RESULT_TRUE : COND_RESULT_FALSE; } else { @@ -298,17 +387,25 @@ static cond_result_t config_check_cond_nocache(server *srv, connection *con, dat } break; } + case COMP_HTTP_SCHEME: + l = con->uri.scheme; + break; + case COMP_HTTP_URL: l = con->uri.path; break; + case COMP_HTTP_QUERY_STRING: + l = con->uri.query; + break; + case COMP_SERVER_SOCKET: l = srv_sock->srv_token; break; case COMP_HTTP_REFERER: { data_string *ds; - + if (NULL != (ds = (data_string *)array_get_element(con->request.headers, "Referer"))) { l = ds->value; } else { @@ -325,7 +422,7 @@ static cond_result_t config_check_cond_nocache(server *srv, connection *con, dat } break; } - case COMP_HTTP_USERAGENT: { + case COMP_HTTP_USER_AGENT: { data_string *ds; if (NULL != (ds = (data_string *)array_get_element(con->request.headers, "User-Agent"))) { l = ds->value; @@ -334,11 +431,30 @@ static cond_result_t config_check_cond_nocache(server *srv, connection *con, dat } break; } + case COMP_HTTP_REQUEST_METHOD: { + const char *method = get_http_method_name(con->request.http_method); + + /* we only have the request method as const char but we need a buffer for comparing */ + + buffer_copy_string(srv->tmp_buf, method); + l = srv->tmp_buf; + + break; + } + case COMP_HTTP_LANGUAGE: { + data_string *ds; + if (NULL != (ds = (data_string *)array_get_element(con->request.headers, "Accept-Language"))) { + l = ds->value; + } else { + l = srv->empty_string; + } + break; + } default: return COND_RESULT_FALSE; } - + if (NULL == l) { if (con->conf.log_condition_handling) { log_error_write(srv, __FILE__, __LINE__, "bsbs", dc->comp_key, @@ -346,7 +462,7 @@ static cond_result_t config_check_cond_nocache(server *srv, connection *con, dat } return COND_RESULT_FALSE; } - + if (con->conf.log_condition_handling) { log_error_write(srv, __FILE__, __LINE__, "bsbsb", dc->comp_key, "(", l, ") compare to ", dc->string); @@ -365,16 +481,17 @@ static cond_result_t config_check_cond_nocache(server *srv, connection *con, dat case CONFIG_COND_MATCH: { cond_cache_t *cache = &con->cond_cache[dc->context_ndx]; int n; - + #ifndef elementsof #define elementsof(x) (sizeof(x) / sizeof(x[0])) #endif n = pcre_exec(dc->regex, dc->regex_study, l->ptr, l->used - 1, 0, 0, cache->matches, elementsof(cache->matches)); - + + cache->patterncount = n; if (n > 0) { - cache->patterncount = n; cache->comp_value = l; + cache->comp_type = dc->comp; return (dc->cond == CONFIG_COND_MATCH) ? COND_RESULT_TRUE : COND_RESULT_FALSE; } else { /* cache is already cleared */ @@ -387,7 +504,7 @@ static cond_result_t config_check_cond_nocache(server *srv, connection *con, dat /* no way */ break; } - + return COND_RESULT_FALSE; } @@ -407,21 +524,56 @@ static cond_result_t config_check_cond_cached(server *srv, connection *con, data } } } + caches[dc->context_ndx].comp_type = dc->comp; + if (con->conf.log_condition_handling) { log_error_write(srv, __FILE__, __LINE__, "dss", dc->context_ndx, "(uncached) result:", - caches[dc->context_ndx].result == COND_RESULT_TRUE ? "true" : "false"); + caches[dc->context_ndx].result == COND_RESULT_UNSET ? "unknown" : + (caches[dc->context_ndx].result == COND_RESULT_TRUE ? "true" : "false")); } } else { if (con->conf.log_condition_handling) { log_error_write(srv, __FILE__, __LINE__, "dss", dc->context_ndx, "(cached) result:", - caches[dc->context_ndx].result == COND_RESULT_TRUE ? "true" : "false"); + caches[dc->context_ndx].result == COND_RESULT_UNSET ? "unknown" : + (caches[dc->context_ndx].result == COND_RESULT_TRUE ? "true" : "false")); } } return caches[dc->context_ndx].result; } +/** + * reset the config-cache for a named item + * + * if the item is COND_LAST_ELEMENT we reset all items + */ +void config_cond_cache_reset_item(server *srv, connection *con, comp_key_t item) { + size_t i; + + for (i = 0; i < srv->config_context->used; i++) { + if (item == COMP_LAST_ELEMENT || + con->cond_cache[i].comp_type == item) { + con->cond_cache[i].result = COND_RESULT_UNSET; + con->cond_cache[i].patterncount = 0; + con->cond_cache[i].comp_value = NULL; + } + } +} + +/** + * reset the config cache to its initial state at connection start + */ +void config_cond_cache_reset(server *srv, connection *con) { + size_t i; + + config_cond_cache_reset_all_items(srv, con); + + for (i = 0; i < COMP_LAST_ELEMENT; i++) { + con->conditional_is_valid[i] = 0; + } +} + int config_check_cond(server *srv, connection *con, data_config *dc) { if (con->conf.log_condition_handling) { log_error_write(srv, __FILE__, __LINE__, "s", "=== start of condition block ==="); @@ -432,7 +584,7 @@ int config_check_cond(server *srv, connection *con, data_config *dc) { int config_append_cond_match_buffer(connection *con, data_config *dc, buffer *buf, int n) { cond_cache_t *cache = &con->cond_cache[dc->context_ndx]; - if (n > cache->patterncount) { + if (n >= cache->patterncount) { return 0; } diff --git a/src/configfile.c b/src/configfile.c index 6f13cd6..bb71b95 100644 --- a/src/configfile.c +++ b/src/configfile.c @@ -1,3 +1,12 @@ +#include "server.h" +#include "log.h" +#include "stream.h" +#include "plugin.h" + +#include "configparser.h" +#include "configfile.h" +#include "proc_open.h" + #include <sys/stat.h> #include <stdlib.h> @@ -7,27 +16,16 @@ #include <string.h> #include <stdio.h> #include <ctype.h> +#include <limits.h> #include <assert.h> -#include "server.h" -#include "log.h" -#include "stream.h" -#include "plugin.h" -#ifdef USE_LICENSE -#include "license.h" -#endif - -#include "configparser.h" -#include "configfile.h" -#include "proc_open.h" - static int config_insert(server *srv) { size_t i; int ret = 0; buffer *stat_cache_string; - - config_values_t cv[] = { + + config_values_t cv[] = { { "server.bind", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_SERVER }, /* 0 */ { "server.errorlog", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_SERVER }, /* 1 */ { "server.errorfile-prefix", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_SERVER }, /* 2 */ @@ -38,49 +36,78 @@ static int config_insert(server *srv) { { "server.tag", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 7 */ { "server.use-ipv6", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 8 */ { "server.modules", NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_SERVER }, /* 9 */ - + { "server.event-handler", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_SERVER }, /* 10 */ { "server.pid-file", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_SERVER }, /* 11 */ - { "server.max-request-size", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 12 */ + { "server.max-request-size", NULL, T_CONFIG_INT, T_CONFIG_SCOPE_CONNECTION }, /* 12 */ { "server.max-worker", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_SERVER }, /* 13 */ { "server.document-root", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 14 */ - { "server.force-lowercase-filenames", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_SERVER }, /* 15 */ + { "server.force-lowercase-filenames", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_SERVER },/* 15 */ { "debug.log-condition-handling", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_SERVER }, /* 16 */ - { "server.max-keep-alive-requests", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 17 */ + { "server.max-keep-alive-requests", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION },/* 17 */ { "server.name", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 18 */ { "server.max-keep-alive-idle", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 19 */ - + { "server.max-read-idle", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 20 */ { "server.max-write-idle", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 21 */ { "server.error-handler-404", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 22 */ { "server.max-fds", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_SERVER }, /* 23 */ +#ifdef HAVE_LSTAT { "server.follow-symlink", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 24 */ +#else + { "server.follow-symlink", + "Your system lacks lstat(). We can not differ symlinks from files." + "Please remove server.follow-symlinks from your config.", + T_CONFIG_UNSUPPORTED, T_CONFIG_SCOPE_UNSET }, /* 24 */ +#endif { "server.kbytes-per-second", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 25 */ { "connection.kbytes-per-second", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 26 */ { "mimetype.use-xattr", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 27 */ { "mimetype.assign", NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION }, /* 28 */ { "ssl.pemfile", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_SERVER }, /* 29 */ - + { "ssl.engine", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_SERVER }, /* 30 */ - + { "debug.log-file-not-found", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_SERVER }, /* 31 */ { "debug.log-request-handling", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_SERVER }, /* 32 */ { "debug.log-response-header", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_SERVER }, /* 33 */ { "debug.log-request-header", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_SERVER }, /* 34 */ - - { "server.protocol-http11", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_SERVER }, /* 35 */ - { "debug.log-request-header-on-error", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_SERVER }, /* 36 */ - { "debug.log-state-handling", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_SERVER }, /* 37 */ - { "ssl.ca-file", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_SERVER }, /* 38 */ - - { "server.errorlog-use-syslog", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_SERVER }, /* 39 */ - { "server.range-requests", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 40 */ - { "server.stat-cache-engine", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 41 */ - { "server.max-connections", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_SERVER }, /* 42 */ - { "server.network-backend", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 43 */ - { "server.upload-dirs", NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION }, /* 44 */ - { "server.core-files", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 45 */ - + { "debug.log-ssl-noise", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_SERVER }, /* 35 */ + + { "server.protocol-http11", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_SERVER }, /* 36 */ + { "debug.log-request-header-on-error", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_SERVER }, /* 37 */ + { "debug.log-state-handling", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_SERVER }, /* 38 */ + { "ssl.ca-file", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_SERVER }, /* 39 */ + + { "server.errorlog-use-syslog", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_SERVER }, /* 40 */ + { "server.range-requests", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 41 */ + { "server.stat-cache-engine", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 42 */ + { "server.max-connections", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_SERVER }, /* 43 */ + { "server.network-backend", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 44 */ + { "server.upload-dirs", NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION }, /* 45 */ + { "server.core-files", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 46 */ + { "ssl.cipher-list", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_SERVER }, /* 47 */ + { "ssl.use-sslv2", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 48 */ + { "etag.use-inode", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_SERVER }, /* 49 */ + { "etag.use-mtime", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_SERVER }, /* 50 */ + { "etag.use-size", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_SERVER }, /* 51 */ + { "server.reject-expect-100-with-417", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_SERVER }, /* 52 */ + { "debug.log-timeouts", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 53 */ + { "server.defer-accept", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 54 */ + { "server.breakagelog", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_SERVER }, /* 55 */ + { "ssl.verifyclient.activate", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_SERVER }, /* 56 */ + { "ssl.verifyclient.enforce", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_SERVER }, /* 57 */ + { "ssl.verifyclient.depth", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_SERVER }, /* 58 */ + { "ssl.verifyclient.username", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_SERVER }, /* 59 */ + { "ssl.verifyclient.exportcert", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_SERVER }, /* 60 */ + + { "server.set-v6only", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 61 */ + { "ssl.use-sslv3", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_SERVER }, /* 62 */ + { "ssl.dh-file", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_SERVER }, /* 63 */ + { "ssl.ec-curve", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_SERVER }, /* 64 */ + { "ssl.disable-client-renegotiation", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_SERVER },/* 65 */ + { "ssl.honor-cipher-order", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_SERVER }, /* 66 */ + { "server.host", "use server.bind instead", T_CONFIG_DEPRECATED, T_CONFIG_SCOPE_UNSET }, { "server.docroot", "use server.document-root instead", T_CONFIG_DEPRECATED, T_CONFIG_SCOPE_UNSET }, { "server.virtual-root", "load mod_simple_vhost and use simple-vhost.server-root instead", T_CONFIG_DEPRECATED, T_CONFIG_SCOPE_UNSET }, @@ -90,11 +117,11 @@ static int config_insert(server *srv) { { "server.groupid", "use server.groupname instead", T_CONFIG_DEPRECATED, T_CONFIG_SCOPE_UNSET }, { "server.use-keep-alive", "use server.max-keep-alive-requests = 0 instead", T_CONFIG_DEPRECATED, T_CONFIG_SCOPE_UNSET }, { "server.force-lower-case-files", "use server.force-lowercase-filenames instead", T_CONFIG_DEPRECATED, T_CONFIG_SCOPE_UNSET }, - + { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET } }; - + /* 0 */ cv[0].destination = srv->srvconf.bindhost; cv[1].destination = srv->srvconf.errorlog_file; @@ -102,33 +129,36 @@ static int config_insert(server *srv) { cv[4].destination = srv->srvconf.username; cv[5].destination = srv->srvconf.groupname; cv[6].destination = &(srv->srvconf.port); - + cv[9].destination = srv->srvconf.modules; cv[10].destination = srv->srvconf.event_handler; cv[11].destination = srv->srvconf.pid_file; - + cv[13].destination = &(srv->srvconf.max_worker); cv[23].destination = &(srv->srvconf.max_fds); - cv[36].destination = &(srv->srvconf.log_request_header_on_error); - cv[37].destination = &(srv->srvconf.log_state_handling); - - cv[39].destination = &(srv->srvconf.errorlog_use_syslog); - + cv[37].destination = &(srv->srvconf.log_request_header_on_error); + cv[38].destination = &(srv->srvconf.log_state_handling); + + cv[40].destination = &(srv->srvconf.errorlog_use_syslog); + stat_cache_string = buffer_init(); - cv[41].destination = stat_cache_string; - cv[43].destination = srv->srvconf.network_backend; - cv[44].destination = srv->srvconf.upload_tempdirs; - cv[45].destination = &(srv->srvconf.enable_cores); - - cv[42].destination = &(srv->srvconf.max_conns); + cv[42].destination = stat_cache_string; + cv[44].destination = srv->srvconf.network_backend; + cv[45].destination = srv->srvconf.upload_tempdirs; + cv[46].destination = &(srv->srvconf.enable_cores); + + cv[43].destination = &(srv->srvconf.max_conns); cv[12].destination = &(srv->srvconf.max_request_size); + cv[52].destination = &(srv->srvconf.reject_expect_100_with_417); + cv[55].destination = srv->srvconf.breakagelog_file; + srv->config_storage = calloc(1, srv->config_context->used * sizeof(specific_config *)); assert(srv->config_storage); - + for (i = 0; i < srv->config_context->used; i++) { specific_config *s; - + s = calloc(1, sizeof(specific_config)); assert(s); s->document_root = buffer_init(); @@ -138,6 +168,9 @@ static int config_insert(server *srv) { s->ssl_ca_file = buffer_init(); s->error_handler = buffer_init(); s->server_tag = buffer_init(); + s->ssl_cipher_list = buffer_init(); + s->ssl_dh_file = buffer_init(); + s->ssl_ec_curve = buffer_init(); s->errorfile_prefix = buffer_init(); s->max_keep_alive_requests = 16; s->max_keep_alive_idle = 5; @@ -145,22 +178,40 @@ static int config_insert(server *srv) { s->max_write_idle = 360; s->use_xattr = 0; s->is_ssl = 0; + s->ssl_honor_cipher_order = 1; + s->ssl_use_sslv2 = 0; + s->ssl_use_sslv3 = 1; s->use_ipv6 = 0; + s->set_v6only = 1; + s->defer_accept = 0; +#ifdef HAVE_LSTAT s->follow_symlink = 1; +#endif s->kbytes_per_second = 0; s->allow_http11 = 1; + s->etag_use_inode = 1; + s->etag_use_mtime = 1; + s->etag_use_size = 1; s->range_requests = 1; - s->force_lowercase_filenames = 0; + s->force_lowercase_filenames = (i == 0) ? 2 : 0; /* we wan't to detect later if user changed this for global section */ s->global_kbytes_per_second = 0; s->global_bytes_per_second_cnt = 0; s->global_bytes_per_second_cnt_ptr = &s->global_bytes_per_second_cnt; - + s->ssl_verifyclient = 0; + s->ssl_verifyclient_enforce = 1; + s->ssl_verifyclient_username = buffer_init(); + s->ssl_verifyclient_depth = 9; + s->ssl_verifyclient_export_cert = 0; + s->ssl_disable_client_renegotiation = 1; + cv[2].destination = s->errorfile_prefix; - + cv[7].destination = s->server_tag; cv[8].destination = &(s->use_ipv6); - - + cv[61].destination = &(s->set_v6only); + cv[54].destination = &(s->defer_accept); + + /* 13 max-worker */ cv[14].destination = s->document_root; cv[15].destination = &(s->force_lowercase_filenames); @@ -171,7 +222,9 @@ static int config_insert(server *srv) { cv[20].destination = &(s->max_read_idle); cv[21].destination = &(s->max_write_idle); cv[22].destination = s->error_handler; +#ifdef HAVE_LSTAT cv[24].destination = &(s->follow_symlink); +#endif /* 23 -> max-fds */ cv[25].destination = &(s->global_kbytes_per_second); cv[26].destination = &(s->kbytes_per_second); @@ -179,48 +232,75 @@ static int config_insert(server *srv) { cv[28].destination = s->mimetypes; cv[29].destination = s->ssl_pemfile; cv[30].destination = &(s->is_ssl); - + cv[31].destination = &(s->log_file_not_found); cv[32].destination = &(s->log_request_handling); cv[33].destination = &(s->log_response_header); cv[34].destination = &(s->log_request_header); - - cv[35].destination = &(s->allow_http11); - cv[38].destination = s->ssl_ca_file; - cv[40].destination = &(s->range_requests); - + cv[35].destination = &(s->log_ssl_noise); + cv[53].destination = &(s->log_timeouts); + + cv[36].destination = &(s->allow_http11); + cv[39].destination = s->ssl_ca_file; + cv[41].destination = &(s->range_requests); + + cv[47].destination = s->ssl_cipher_list; + cv[48].destination = &(s->ssl_use_sslv2); + cv[62].destination = &(s->ssl_use_sslv3); + cv[63].destination = s->ssl_dh_file; + cv[64].destination = s->ssl_ec_curve; + cv[66].destination = &(s->ssl_honor_cipher_order); + + cv[49].destination = &(s->etag_use_inode); + cv[50].destination = &(s->etag_use_mtime); + cv[51].destination = &(s->etag_use_size); + + /* ssl.verify */ + cv[56].destination = &(s->ssl_verifyclient); + cv[57].destination = &(s->ssl_verifyclient_enforce); + cv[58].destination = &(s->ssl_verifyclient_depth); + cv[59].destination = s->ssl_verifyclient_username; + cv[60].destination = &(s->ssl_verifyclient_export_cert); + cv[65].destination = &(s->ssl_disable_client_renegotiation); + srv->config_storage[i] = s; - + if (0 != (ret = config_insert_values_global(srv, ((data_config *)srv->config_context->data[i])->value, cv))) { break; } } - + if (buffer_is_empty(stat_cache_string)) { srv->srvconf.stat_cache_engine = STAT_CACHE_ENGINE_SIMPLE; } else if (buffer_is_equal_string(stat_cache_string, CONST_STR_LEN("simple"))) { srv->srvconf.stat_cache_engine = STAT_CACHE_ENGINE_SIMPLE; +#ifdef HAVE_FAM_H } else if (buffer_is_equal_string(stat_cache_string, CONST_STR_LEN("fam"))) { srv->srvconf.stat_cache_engine = STAT_CACHE_ENGINE_FAM; +#endif } else if (buffer_is_equal_string(stat_cache_string, CONST_STR_LEN("disable"))) { srv->srvconf.stat_cache_engine = STAT_CACHE_ENGINE_NONE; } else { - log_error_write(srv, __FILE__, __LINE__, "sb", - "server.stat-cache-engine can be one of \"disable\", \"simple\", \"fam\", but not:", stat_cache_string); + log_error_write(srv, __FILE__, __LINE__, "sb", + "server.stat-cache-engine can be one of \"disable\", \"simple\"," +#ifdef HAVE_FAM_H + " \"fam\"," +#endif + " but not:", stat_cache_string); ret = HANDLER_ERROR; } - + buffer_free(stat_cache_string); - + return ret; - + } #define PATCH(x) con->conf.x = s->x int config_setup_connection(server *srv, connection *con) { specific_config *s = srv->config_storage[0]; - + PATCH(allow_http11); PATCH(mimetypes); PATCH(document_root); @@ -231,48 +311,71 @@ int config_setup_connection(server *srv, connection *con) { PATCH(use_xattr); PATCH(error_handler); PATCH(errorfile_prefix); +#ifdef HAVE_LSTAT PATCH(follow_symlink); +#endif PATCH(server_tag); PATCH(kbytes_per_second); PATCH(global_kbytes_per_second); PATCH(global_bytes_per_second_cnt); - + con->conf.global_bytes_per_second_cnt_ptr = &s->global_bytes_per_second_cnt; buffer_copy_string_buffer(con->server_name, s->server_name); - + PATCH(log_request_header); PATCH(log_response_header); PATCH(log_request_handling); PATCH(log_condition_handling); PATCH(log_file_not_found); - + PATCH(log_ssl_noise); + PATCH(log_timeouts); + PATCH(range_requests); PATCH(force_lowercase_filenames); PATCH(is_ssl); - + PATCH(ssl_pemfile); +#ifdef USE_OPENSSL + PATCH(ssl_ctx); +#endif PATCH(ssl_ca_file); + PATCH(ssl_cipher_list); + PATCH(ssl_dh_file); + PATCH(ssl_ec_curve); + PATCH(ssl_honor_cipher_order); + PATCH(ssl_use_sslv2); + PATCH(ssl_use_sslv3); + PATCH(etag_use_inode); + PATCH(etag_use_mtime); + PATCH(etag_use_size); + + PATCH(ssl_verifyclient); + PATCH(ssl_verifyclient_enforce); + PATCH(ssl_verifyclient_depth); + PATCH(ssl_verifyclient_username); + PATCH(ssl_verifyclient_export_cert); + PATCH(ssl_disable_client_renegotiation); + return 0; } int config_patch_connection(server *srv, connection *con, comp_key_t comp) { size_t i, j; - + + con->conditional_is_valid[comp] = 1; + /* skip the first, the global context */ for (i = 1; i < srv->config_context->used; i++) { data_config *dc = (data_config *)srv->config_context->data[i]; specific_config *s = srv->config_storage[i]; - - /* not our stage */ - if (comp != dc->comp) continue; - + /* condition didn't match */ if (!config_check_cond(srv, con, dc)) continue; - + /* merge config */ for (j = 0; j < dc->value->used; j++) { data_unset *du = dc->value->data[j]; - + if (buffer_is_equal_string(du->key, CONST_STR_LEN("server.document-root"))) { PATCH(document_root); } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("server.range-requests"))) { @@ -293,14 +396,37 @@ int config_patch_connection(server *srv, connection *con, comp_key_t comp) { PATCH(max_read_idle); } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("mimetype.use-xattr"))) { PATCH(use_xattr); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("etag.use-inode"))) { + PATCH(etag_use_inode); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("etag.use-mtime"))) { + PATCH(etag_use_mtime); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("etag.use-size"))) { + PATCH(etag_use_size); } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("ssl.pemfile"))) { PATCH(ssl_pemfile); +#ifdef USE_OPENSSL + PATCH(ssl_ctx); +#endif } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("ssl.ca-file"))) { PATCH(ssl_ca_file); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("ssl.honor-cipher-order"))) { + PATCH(ssl_honor_cipher_order); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("ssl.use-sslv2"))) { + PATCH(ssl_use_sslv2); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("ssl.use-sslv3"))) { + PATCH(ssl_use_sslv3); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("ssl.cipher-list"))) { + PATCH(ssl_cipher_list); } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("ssl.engine"))) { PATCH(is_ssl); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("ssl.dh-file"))) { + PATCH(ssl_dh_file); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("ssl.ec-curve"))) { + PATCH(ssl_ec_curve); +#ifdef HAVE_LSTAT } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("server.follow-symlink"))) { PATCH(follow_symlink); +#endif } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("server.name"))) { buffer_copy_string_buffer(con->server_name, s->server_name); } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("server.tag"))) { @@ -317,18 +443,38 @@ int config_patch_connection(server *srv, connection *con, comp_key_t comp) { PATCH(log_condition_handling); } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("debug.log-file-not-found"))) { PATCH(log_file_not_found); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("debug.log-ssl-noise"))) { + PATCH(log_ssl_noise); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("debug.log-timeouts"))) { + PATCH(log_timeouts); } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("server.protocol-http11"))) { PATCH(allow_http11); - } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("server.force-lowercase-filenames"))) { + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("server.force-lowercase-filenames"))) { PATCH(force_lowercase_filenames); } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("server.kbytes-per-second"))) { PATCH(global_kbytes_per_second); PATCH(global_bytes_per_second_cnt); con->conf.global_bytes_per_second_cnt_ptr = &s->global_bytes_per_second_cnt; + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("ssl.verifyclient.activate"))) { + PATCH(ssl_verifyclient); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("ssl.verifyclient.enforce"))) { + PATCH(ssl_verifyclient_enforce); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("ssl.verifyclient.depth"))) { + PATCH(ssl_verifyclient_depth); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("ssl.verifyclient.username"))) { + PATCH(ssl_verifyclient_username); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("ssl.verifyclient.exportcert"))) { + PATCH(ssl_verifyclient_export_cert); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("ssl.disable-client-renegotiation"))) { + PATCH(ssl_disable_client_renegotiation); } } } - + + con->etag_flags = (con->conf.etag_use_mtime ? ETAG_USE_MTIME : 0) | + (con->conf.etag_use_inode ? ETAG_USE_INODE : 0) | + (con->conf.etag_use_size ? ETAG_USE_SIZE : 0); + return 0; } #undef PATCH @@ -336,15 +482,15 @@ int config_patch_connection(server *srv, connection *con, comp_key_t comp) { typedef struct { int foo; int bar; - + const buffer *source; const char *input; size_t offset; size_t size; - + int line_pos; int line; - + int in_key; int in_brace; int in_cond; @@ -352,8 +498,8 @@ typedef struct { #if 0 static int tokenizer_open(server *srv, tokenizer_t *t, buffer *basedir, const char *fn) { - if (buffer_is_empty(basedir) && - (fn[0] == '/' || fn[0] == '\\') && + if (buffer_is_empty(basedir) || + (fn[0] == '/' || fn[0] == '\\') || (fn[0] == '.' && (fn[1] == '/' || fn[1] == '\\'))) { t->file = buffer_init_string(fn); } else { @@ -362,7 +508,7 @@ static int tokenizer_open(server *srv, tokenizer_t *t, buffer *basedir, const ch } if (0 != stream_open(&(t->s), t->file)) { - log_error_write(srv, __FILE__, __LINE__, "sbss", + log_error_write(srv, __FILE__, __LINE__, "sbss", "opening configfile ", t->file, "failed:", strerror(errno)); buffer_free(t->file); return -1; @@ -373,7 +519,7 @@ static int tokenizer_open(server *srv, tokenizer_t *t, buffer *basedir, const ch t->size = t->s.size; t->line = 1; t->line_pos = 1; - + t->in_key = 1; t->in_brace = 0; t->in_cond = 0; @@ -401,7 +547,7 @@ static int config_skip_newline(tokenizer_t *t) { static int config_skip_comment(tokenizer_t *t) { int i; assert(t->input[t->offset] == '#'); - for (i = 1; t->input[t->offset + i] && + for (i = 1; t->input[t->offset + i] && (t->input[t->offset + i] != '\n' && t->input[t->offset + i] != '\r'); i++); t->offset += i; @@ -411,96 +557,96 @@ static int config_skip_comment(tokenizer_t *t) { static int config_tokenizer(server *srv, tokenizer_t *t, int *token_id, buffer *token) { int tid = 0; size_t i; - + for (tid = 0; tid == 0 && t->offset < t->size && t->input[t->offset] ; ) { char c = t->input[t->offset]; const char *start = NULL; - + switch (c) { - case '=': + case '=': if (t->in_brace) { if (t->input[t->offset + 1] == '>') { t->offset += 2; - - buffer_copy_string(token, "=>"); - + + buffer_copy_string_len(token, CONST_STR_LEN("=>")); + tid = TK_ARRAY_ASSIGN; } else { - log_error_write(srv, __FILE__, __LINE__, "sbsdsds", + log_error_write(srv, __FILE__, __LINE__, "sbsdsds", "source:", t->source, - "line:", t->line, "pos:", t->line_pos, + "line:", t->line, "pos:", t->line_pos, "use => for assignments in arrays"); return -1; } } else if (t->in_cond) { if (t->input[t->offset + 1] == '=') { t->offset += 2; - - buffer_copy_string(token, "=="); - + + buffer_copy_string_len(token, CONST_STR_LEN("==")); + tid = TK_EQ; } else if (t->input[t->offset + 1] == '~') { t->offset += 2; - - buffer_copy_string(token, "=~"); - + + buffer_copy_string_len(token, CONST_STR_LEN("=~")); + tid = TK_MATCH; } else { - log_error_write(srv, __FILE__, __LINE__, "sbsdsds", + log_error_write(srv, __FILE__, __LINE__, "sbsdsds", "source:", t->source, - "line:", t->line, "pos:", t->line_pos, - "only =~ and == are allow in the condition"); + "line:", t->line, "pos:", t->line_pos, + "only =~ and == are allowed in the condition"); return -1; } t->in_key = 1; t->in_cond = 0; } else if (t->in_key) { tid = TK_ASSIGN; - + buffer_copy_string_len(token, t->input + t->offset, 1); - + t->offset++; t->line_pos++; } else { - log_error_write(srv, __FILE__, __LINE__, "sbsdsds", + log_error_write(srv, __FILE__, __LINE__, "sbsdsds", "source:", t->source, - "line:", t->line, "pos:", t->line_pos, + "line:", t->line, "pos:", t->line_pos, "unexpected equal-sign: ="); return -1; } - + break; - case '!': + case '!': if (t->in_cond) { if (t->input[t->offset + 1] == '=') { t->offset += 2; - - buffer_copy_string(token, "!="); - + + buffer_copy_string_len(token, CONST_STR_LEN("!=")); + tid = TK_NE; } else if (t->input[t->offset + 1] == '~') { t->offset += 2; - - buffer_copy_string(token, "!~"); - + + buffer_copy_string_len(token, CONST_STR_LEN("!~")); + tid = TK_NOMATCH; } else { - log_error_write(srv, __FILE__, __LINE__, "sbsdsds", + log_error_write(srv, __FILE__, __LINE__, "sbsdsds", "source:", t->source, - "line:", t->line, "pos:", t->line_pos, - "only !~ and != are allow in the condition"); + "line:", t->line, "pos:", t->line_pos, + "only !~ and != are allowed in the condition"); return -1; } t->in_key = 1; t->in_cond = 0; } else { - log_error_write(srv, __FILE__, __LINE__, "sbsdsds", + log_error_write(srv, __FILE__, __LINE__, "sbsdsds", "source:", t->source, - "line:", t->line, "pos:", t->line_pos, + "line:", t->line, "pos:", t->line_pos, "unexpected exclamation-marks: !"); return -1; } - + break; case '\t': case ' ': @@ -536,214 +682,208 @@ static int config_tokenizer(server *srv, tokenizer_t *t, int *token_id, buffer * } t->in_key = 1; tid = TK_EOL; - buffer_copy_string(token, "(EOL)"); + buffer_copy_string_len(token, CONST_STR_LEN("(EOL)")); } else { config_skip_newline(t); t->line_pos = 1; - t->line++; + t->line++; } break; case ',': if (t->in_brace > 0) { tid = TK_COMMA; - - buffer_copy_string(token, "(COMMA)"); + + buffer_copy_string_len(token, CONST_STR_LEN("(COMMA)")); } - + t->offset++; t->line_pos++; break; case '"': /* search for the terminating " */ start = t->input + t->offset + 1; - buffer_copy_string(token, ""); - + buffer_copy_string_len(token, CONST_STR_LEN("")); + for (i = 1; t->input[t->offset + i]; i++) { if (t->input[t->offset + i] == '\\' && t->input[t->offset + i + 1] == '"') { - + buffer_append_string_len(token, start, t->input + t->offset + i - start); - + start = t->input + t->offset + i + 1; - + /* skip the " */ i++; continue; } - - + + if (t->input[t->offset + i] == '"') { tid = TK_STRING; - + buffer_append_string_len(token, start, t->input + t->offset + i - start); - + break; } } if (t->input[t->offset + i] == '\0') { /* ERROR */ - - log_error_write(srv, __FILE__, __LINE__, "sbsdsds", + + log_error_write(srv, __FILE__, __LINE__, "sbsdsds", "source:", t->source, - "line:", t->line, "pos:", t->line_pos, + "line:", t->line, "pos:", t->line_pos, "missing closing quote"); - + return -1; } - + t->offset += i + 1; t->line_pos += i + 1; - + break; case '(': t->offset++; t->in_brace++; - + tid = TK_LPARAN; - - buffer_copy_string(token, "("); + + buffer_copy_string_len(token, CONST_STR_LEN("(")); break; case ')': t->offset++; t->in_brace--; - + tid = TK_RPARAN; - - buffer_copy_string(token, ")"); + + buffer_copy_string_len(token, CONST_STR_LEN(")")); break; case '$': t->offset++; - + tid = TK_DOLLAR; t->in_cond = 1; t->in_key = 0; - - buffer_copy_string(token, "$"); - + + buffer_copy_string_len(token, CONST_STR_LEN("$")); + break; case '+': if (t->input[t->offset + 1] == '=') { t->offset += 2; - buffer_copy_string(token, "+="); + buffer_copy_string_len(token, CONST_STR_LEN("+=")); tid = TK_APPEND; } else { t->offset++; tid = TK_PLUS; - buffer_copy_string(token, "+"); + buffer_copy_string_len(token, CONST_STR_LEN("+")); } break; case '{': t->offset++; - + tid = TK_LCURLY; - - buffer_copy_string(token, "{"); - + + buffer_copy_string_len(token, CONST_STR_LEN("{")); + break; - + case '}': t->offset++; - + tid = TK_RCURLY; - - buffer_copy_string(token, "}"); - + + buffer_copy_string_len(token, CONST_STR_LEN("}")); + break; case '[': t->offset++; - + tid = TK_LBRACKET; - - buffer_copy_string(token, "["); - + + buffer_copy_string_len(token, CONST_STR_LEN("[")); + break; - + case ']': t->offset++; - + tid = TK_RBRACKET; - - buffer_copy_string(token, "]"); - + + buffer_copy_string_len(token, CONST_STR_LEN("]")); + break; case '#': t->line_pos += config_skip_comment(t); - + break; default: if (t->in_cond) { - for (i = 0; t->input[t->offset + i] && + for (i = 0; t->input[t->offset + i] && (isalpha((unsigned char)t->input[t->offset + i]) ); i++); - + if (i && t->input[t->offset + i]) { tid = TK_SRVVARNAME; buffer_copy_string_len(token, t->input + t->offset, i); - + t->offset += i; t->line_pos += i; } else { /* ERROR */ - log_error_write(srv, __FILE__, __LINE__, "sbsdsds", + log_error_write(srv, __FILE__, __LINE__, "sbsdsds", "source:", t->source, - "line:", t->line, "pos:", t->line_pos, + "line:", t->line, "pos:", t->line_pos, "invalid character in condition"); return -1; } } else if (isdigit((unsigned char)c)) { /* take all digits */ for (i = 0; t->input[t->offset + i] && isdigit((unsigned char)t->input[t->offset + i]); i++); - + /* was there it least a digit ? */ - if (i && t->input[t->offset + i]) { + if (i) { tid = TK_INTEGER; - + buffer_copy_string_len(token, t->input + t->offset, i); - + t->offset += i; t->line_pos += i; - } else { - /* ERROR */ - log_error_write(srv, __FILE__, __LINE__, "sbsdsds", - "source:", t->source, - "line:", t->line, "pos:", t->line_pos, - "unexpected EOF"); - - return -1; } } else { /* the key might consist of [-.0-9a-z] */ - for (i = 0; t->input[t->offset + i] && - (isalnum((unsigned char)t->input[t->offset + i]) || + for (i = 0; t->input[t->offset + i] && + (isalnum((unsigned char)t->input[t->offset + i]) || t->input[t->offset + i] == '.' || t->input[t->offset + i] == '_' || /* for env.* */ t->input[t->offset + i] == '-' ); i++); - + if (i && t->input[t->offset + i]) { buffer_copy_string_len(token, t->input + t->offset, i); - + if (strcmp(token->ptr, "include") == 0) { tid = TK_INCLUDE; } else if (strcmp(token->ptr, "include_shell") == 0) { tid = TK_INCLUDE_SHELL; + } else if (strcmp(token->ptr, "global") == 0) { + tid = TK_GLOBAL; } else if (strcmp(token->ptr, "else") == 0) { tid = TK_ELSE; } else { tid = TK_LKEY; } - + t->offset += i; t->line_pos += i; } else { /* ERROR */ - log_error_write(srv, __FILE__, __LINE__, "sbsdsds", + log_error_write(srv, __FILE__, __LINE__, "sbsdsds", "source:", t->source, - "line:", t->line, "pos:", t->line_pos, + "line:", t->line, "pos:", t->line_pos, "invalid character in variable name"); return -1; } @@ -751,16 +891,16 @@ static int config_tokenizer(server *srv, tokenizer_t *t, int *token_id, buffer * break; } } - + if (tid) { *token_id = tid; #if 0 - log_error_write(srv, __FILE__, __LINE__, "sbsdsdbdd", + log_error_write(srv, __FILE__, __LINE__, "sbsdsdbdd", "source:", t->source, "line:", t->line, "pos:", t->line_pos, token, token->used - 1, tid); #endif - + return 1; } else if (t->offset < t->size) { fprintf(stderr, "%s.%d: %d, %s\n", @@ -782,7 +922,7 @@ static int config_parse(server *srv, config_t *context, tokenizer_t *t) { while((1 == (ret = config_tokenizer(srv, t, &token_id, token))) && context->ok) { buffer_copy_string_buffer(lasttoken, token); configparser(pParser, token_id, token, context); - + token = buffer_init(); } buffer_free(token); @@ -795,14 +935,14 @@ static int config_parse(server *srv, config_t *context, tokenizer_t *t) { } } configparserFree(pParser, free); - + if (ret == -1) { - log_error_write(srv, __FILE__, __LINE__, "sb", - "configfile parser failed:", lasttoken); + log_error_write(srv, __FILE__, __LINE__, "sb", + "configfile parser failed at:", lasttoken); } else if (context->ok == 0) { - log_error_write(srv, __FILE__, __LINE__, "sbsdsdsb", + log_error_write(srv, __FILE__, __LINE__, "sbsdsdsb", "source:", t->source, - "line:", t->line, "pos:", t->line_pos, + "line:", t->line, "pos:", t->line_pos, "parser failed somehow near here:", lasttoken); ret = -1; } @@ -819,7 +959,7 @@ static int tokenizer_init(tokenizer_t *t, const buffer *source, const char *inpu t->offset = 0; t->line = 1; t->line_pos = 1; - + t->in_key = 1; t->in_brace = 0; t->in_cond = 0; @@ -832,8 +972,8 @@ int config_parse_file(server *srv, config_t *context, const char *fn) { int ret; buffer *filename; - if (buffer_is_empty(context->basedir) && - (fn[0] == '/' || fn[0] == '\\') && + if (buffer_is_empty(context->basedir) || + (fn[0] == '/' || fn[0] == '\\') || (fn[0] == '.' && (fn[1] == '/' || fn[1] == '\\'))) { filename = buffer_init_string(fn); } else { @@ -842,9 +982,14 @@ int config_parse_file(server *srv, config_t *context, const char *fn) { } if (0 != stream_open(&s, filename)) { - log_error_write(srv, __FILE__, __LINE__, "sbss", - "opening configfile ", filename, "failed:", strerror(errno)); - ret = -1; + if (s.size == 0) { + /* the file was empty, nothing to parse */ + ret = 0; + } else { + log_error_write(srv, __FILE__, __LINE__, "sbss", + "opening configfile ", filename, "failed:", strerror(errno)); + ret = -1; + } } else { tokenizer_init(&t, filename, s.start, s.size); ret = config_parse(srv, context, &t); @@ -855,16 +1000,39 @@ int config_parse_file(server *srv, config_t *context, const char *fn) { return ret; } +static char* getCWD(void) { + char *s, *s1; + size_t len; +#ifdef PATH_MAX + len = PATH_MAX; +#else + len = 4096; +#endif + + s = malloc(len); + if (!s) return NULL; + while (NULL == getcwd(s, len)) { + if (errno != ERANGE || SSIZE_MAX - len < len) return NULL; + len *= 2; + s1 = realloc(s, len); + if (!s1) { + free(s); + return NULL; + } + s = s1; + } + return s; +} + int config_parse_cmd(server *srv, config_t *context, const char *cmd) { - proc_handler_t proc; tokenizer_t t; int ret; buffer *source; buffer *out; - char oldpwd[PATH_MAX]; + char *oldpwd; - if (NULL == getcwd(oldpwd, sizeof(oldpwd))) { - log_error_write(srv, __FILE__, __LINE__, "s", + if (NULL == (oldpwd = getCWD())) { + log_error_write(srv, __FILE__, __LINE__, "s", "cannot get cwd", strerror(errno)); return -1; } @@ -876,8 +1044,8 @@ int config_parse_cmd(server *srv, config_t *context, const char *cmd) { chdir(context->basedir->ptr); } - if (0 != proc_open_buffer(&proc, cmd, NULL, out, NULL)) { - log_error_write(srv, __FILE__, __LINE__, "sbss", + if (0 != proc_open_buffer(cmd, NULL, out, NULL)) { + log_error_write(srv, __FILE__, __LINE__, "sbss", "opening", source, "failed:", strerror(errno)); ret = -1; } else { @@ -888,6 +1056,7 @@ int config_parse_cmd(server *srv, config_t *context, const char *cmd) { buffer_free(source); buffer_free(out); chdir(oldpwd); + free(oldpwd); return ret; } @@ -908,6 +1077,7 @@ int config_read(server *srv, const char *fn) { config_t context; data_config *dc; data_integer *dpid; + data_string *dcwd; int ret; char *pos; data_array *modules; @@ -915,20 +1085,18 @@ int config_read(server *srv, const char *fn) { context_init(srv, &context); context.all_configs = srv->config_context; - pos = strrchr(fn, #ifdef __WIN32 - '\\' + pos = strrchr(fn, '\\'); #else - '/' + pos = strrchr(fn, '/'); #endif - ); if (pos) { buffer_copy_string_len(context.basedir, fn, pos - fn + 1); fn = pos + 1; } - + dc = data_config_init(); - buffer_copy_string(dc->key, "global"); + buffer_copy_string_len(dc->key, CONST_STR_LEN("global")); assert(context.all_configs->used == 0); dc->context_ndx = context.all_configs->used; @@ -939,9 +1107,17 @@ int config_read(server *srv, const char *fn) { srv->config = dc->value; dpid = data_integer_init(); dpid->value = getpid(); - buffer_copy_string(dpid->key, "var.PID"); + buffer_copy_string_len(dpid->key, CONST_STR_LEN("var.PID")); array_insert_unique(srv->config, (data_unset *)dpid); - + + dcwd = data_string_init(); + buffer_prepare_copy(dcwd->value, 1024); + if (NULL != getcwd(dcwd->value->ptr, dcwd->value->size - 1)) { + dcwd->value->used = strlen(dcwd->value->ptr) + 1; + buffer_copy_string_len(dcwd->key, CONST_STR_LEN("var.CWD")); + array_insert_unique(srv->config, (data_unset *)dcwd); + } + ret = config_parse_file(srv, &context, fn); /* remains nothing if parser is ok */ @@ -957,7 +1133,7 @@ int config_read(server *srv, const char *fn) { } else { return -1; } - + if (NULL != (modules = (data_array *)array_get_element(srv->config, "server.modules"))) { data_string *ds; data_array *prepends; @@ -972,7 +1148,7 @@ int config_read(server *srv, const char *fn) { /* prepend default modules */ if (NULL == array_get_element(modules->value, "mod_indexfile")) { ds = data_string_init(); - buffer_copy_string(ds->value, "mod_indexfile"); + buffer_copy_string_len(ds->value, CONST_STR_LEN("mod_indexfile")); array_insert_unique(prepends->value, (data_unset *)ds); } @@ -985,22 +1161,42 @@ int config_read(server *srv, const char *fn) { /* append default modules */ if (NULL == array_get_element(modules->value, "mod_dirlisting")) { ds = data_string_init(); - buffer_copy_string(ds->value, "mod_dirlisting"); + buffer_copy_string_len(ds->value, CONST_STR_LEN("mod_dirlisting")); array_insert_unique(modules->value, (data_unset *)ds); } if (NULL == array_get_element(modules->value, "mod_staticfile")) { ds = data_string_init(); - buffer_copy_string(ds->value, "mod_staticfile"); + buffer_copy_string_len(ds->value, CONST_STR_LEN("mod_staticfile")); array_insert_unique(modules->value, (data_unset *)ds); } + } else { + data_string *ds; + + modules = data_array_init(); + + /* server.modules is not set */ + ds = data_string_init(); + buffer_copy_string_len(ds->value, CONST_STR_LEN("mod_indexfile")); + array_insert_unique(modules->value, (data_unset *)ds); + + ds = data_string_init(); + buffer_copy_string_len(ds->value, CONST_STR_LEN("mod_dirlisting")); + array_insert_unique(modules->value, (data_unset *)ds); + + ds = data_string_init(); + buffer_copy_string_len(ds->value, CONST_STR_LEN("mod_staticfile")); + array_insert_unique(modules->value, (data_unset *)ds); + + buffer_copy_string_len(modules->key, CONST_STR_LEN("server.modules")); + array_insert_unique(srv->config, (data_unset *)modules); } - + if (0 != config_insert(srv)) { return -1; } - + return 0; } @@ -1008,48 +1204,50 @@ int config_set_defaults(server *srv) { size_t i; specific_config *s = srv->config_storage[0]; struct stat st1, st2; - - struct ev_map { fdevent_handler_t et; const char *name; } event_handlers[] = - { - /* - poll is most reliable + + struct ev_map { fdevent_handler_t et; const char *name; } event_handlers[] = + { + /* - epoll is most reliable * - select works everywhere - * - linux-* are experimental */ +#ifdef USE_LINUX_EPOLL + { FDEVENT_HANDLER_LINUX_SYSEPOLL, "linux-sysepoll" }, +#endif #ifdef USE_POLL { FDEVENT_HANDLER_POLL, "poll" }, #endif #ifdef USE_SELECT { FDEVENT_HANDLER_SELECT, "select" }, #endif -#ifdef USE_LINUX_EPOLL - { FDEVENT_HANDLER_LINUX_SYSEPOLL, "linux-sysepoll" }, -#endif -#ifdef USE_LINUX_SIGIO - { FDEVENT_HANDLER_LINUX_RTSIG, "linux-rtsig" }, +#ifdef USE_LIBEV + { FDEVENT_HANDLER_LIBEV, "libev" }, #endif #ifdef USE_SOLARIS_DEVPOLL { FDEVENT_HANDLER_SOLARIS_DEVPOLL,"solaris-devpoll" }, #endif +#ifdef USE_SOLARIS_PORT + { FDEVENT_HANDLER_SOLARIS_PORT, "solaris-eventports" }, +#endif #ifdef USE_FREEBSD_KQUEUE { FDEVENT_HANDLER_FREEBSD_KQUEUE, "freebsd-kqueue" }, { FDEVENT_HANDLER_FREEBSD_KQUEUE, "kqueue" }, #endif { FDEVENT_HANDLER_UNSET, NULL } }; - - - if (buffer_is_empty(s->document_root)) { - log_error_write(srv, __FILE__, __LINE__, "s", - "a default document-root has to be set"); - - return -1; - } - + + + if (buffer_is_empty(s->document_root)) { + log_error_write(srv, __FILE__, __LINE__, "s", + "a default document-root has to be set"); + + return -1; + } + if (buffer_is_empty(srv->srvconf.changeroot)) { - if (-1 == stat(s->document_root->ptr, &st1)) { - log_error_write(srv, __FILE__, __LINE__, "sb", + if (-1 == stat(s->document_root->ptr, &st1)) { + log_error_write(srv, __FILE__, __LINE__, "sb", "base-docroot doesn't exist:", - s->document_root); + s->document_root); return -1; } @@ -1057,87 +1255,91 @@ int config_set_defaults(server *srv) { buffer_copy_string_buffer(srv->tmp_buf, srv->srvconf.changeroot); buffer_append_string_buffer(srv->tmp_buf, s->document_root); - if (-1 == stat(srv->tmp_buf->ptr, &st1)) { - log_error_write(srv, __FILE__, __LINE__, "sb", + if (-1 == stat(srv->tmp_buf->ptr, &st1)) { + log_error_write(srv, __FILE__, __LINE__, "sb", "base-docroot doesn't exist:", - srv->tmp_buf); + srv->tmp_buf); return -1; } - + } - - buffer_copy_string_buffer(srv->tmp_buf, s->document_root); - buffer_to_lower(srv->tmp_buf); + buffer_copy_string_buffer(srv->tmp_buf, s->document_root); + + buffer_to_lower(srv->tmp_buf); + + if (2 == s->force_lowercase_filenames) { /* user didn't configure it in global section? */ + s->force_lowercase_filenames = 0; /* default to 0 */ - if (0 == stat(srv->tmp_buf->ptr, &st1)) { - int is_lower = 0; + if (0 == stat(srv->tmp_buf->ptr, &st1)) { + int is_lower = 0; - is_lower = buffer_is_equal(srv->tmp_buf, s->document_root); + is_lower = buffer_is_equal(srv->tmp_buf, s->document_root); - /* lower-case existed, check upper-case */ - buffer_copy_string_buffer(srv->tmp_buf, s->document_root); + /* lower-case existed, check upper-case */ + buffer_copy_string_buffer(srv->tmp_buf, s->document_root); - buffer_to_upper(srv->tmp_buf); + buffer_to_upper(srv->tmp_buf); - /* we have to handle the special case that upper and lower-casing results in the same filename - * as in server.document-root = "/" or "/12345/" */ + /* we have to handle the special case that upper and lower-casing results in the same filename + * as in server.document-root = "/" or "/12345/" */ - if (is_lower && buffer_is_equal(srv->tmp_buf, s->document_root)) { - /* lower-casing and upper-casing didn't result in - * an other filename, no need to stat(), - * just assume it is case-sensitive. */ + if (is_lower && buffer_is_equal(srv->tmp_buf, s->document_root)) { + /* lower-casing and upper-casing didn't result in + * an other filename, no need to stat(), + * just assume it is case-sensitive. */ - s->force_lowercase_filenames = 0; - } else if (0 == stat(srv->tmp_buf->ptr, &st2)) { + s->force_lowercase_filenames = 0; + } else if (0 == stat(srv->tmp_buf->ptr, &st2)) { + + /* upper case exists too, doesn't the FS handle this ? */ + + /* upper and lower have the same inode -> case-insensitve FS */ + + if (st1.st_ino == st2.st_ino) { + /* upper and lower have the same inode -> case-insensitve FS */ + + s->force_lowercase_filenames = 1; + } + } + } + } - /* upper case exists too, doesn't the FS handle this ? */ - - /* upper and lower have the same inode -> case-insensitve FS */ - - if (st1.st_ino == st2.st_ino) { - /* upper and lower have the same inode -> case-insensitve FS */ - - s->force_lowercase_filenames = 1; - } - } - } - if (srv->srvconf.port == 0) { srv->srvconf.port = s->is_ssl ? 443 : 80; } - + if (srv->srvconf.event_handler->used == 0) { /* choose a good default - * - * the event_handler list is sorted by 'goodness' + * + * the event_handler list is sorted by 'goodness' * taking the first available should be the best solution */ srv->event_handler = event_handlers[0].et; - + if (FDEVENT_HANDLER_UNSET == srv->event_handler) { - log_error_write(srv, __FILE__, __LINE__, "s", + log_error_write(srv, __FILE__, __LINE__, "s", "sorry, there is no event handler for this system"); - + return -1; } } else { /* * User override */ - + for (i = 0; event_handlers[i].name; i++) { if (0 == strcmp(event_handlers[i].name, srv->srvconf.event_handler->ptr)) { srv->event_handler = event_handlers[i].et; break; } } - + if (FDEVENT_HANDLER_UNSET == srv->event_handler) { - log_error_write(srv, __FILE__, __LINE__, "sb", - "the selected event-handler in unknown or not supported:", + log_error_write(srv, __FILE__, __LINE__, "sb", + "the selected event-handler in unknown or not supported:", srv->srvconf.event_handler ); - + return -1; } } @@ -1145,19 +1347,19 @@ int config_set_defaults(server *srv) { if (s->is_ssl) { if (buffer_is_empty(s->ssl_pemfile)) { /* PEM file is require */ - - log_error_write(srv, __FILE__, __LINE__, "s", + + log_error_write(srv, __FILE__, __LINE__, "s", "ssl.pemfile has to be set"); return -1; } - + #ifndef USE_OPENSSL - log_error_write(srv, __FILE__, __LINE__, "s", + log_error_write(srv, __FILE__, __LINE__, "s", "ssl support is missing, recompile with --with-openssl"); - + return -1; #endif } - + return 0; } diff --git a/src/configfile.h b/src/configfile.h index 600297f..f46e869 100644 --- a/src/configfile.h +++ b/src/configfile.h @@ -21,4 +21,10 @@ int config_parse_file(server *srv, config_t *context, const char *fn); int config_parse_cmd(server *srv, config_t *context, const char *cmd); data_unset *configparser_merge_data(data_unset *op1, const data_unset *op2); +void config_cond_cache_reset(server *srv, connection *con); +void config_cond_cache_reset_item(server *srv, connection *con, comp_key_t item); + +#define config_cond_cache_reset_all_items(srv, con) \ + config_cond_cache_reset_item(srv, con, COMP_LAST_ELEMENT); + #endif diff --git a/src/configparser.c b/src/configparser.c index 2ce169a..a13d729 100644 --- a/src/configparser.c +++ b/src/configparser.c @@ -4,18 +4,16 @@ /* First off, code is include which follows the "include" declaration ** in the input file. */ #include <stdio.h> -#line 5 "./configparser.y" +#line 5 "../../src/configparser.y" -#include <assert.h> -#include <stdio.h> -#include <string.h> -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif #include "configfile.h" #include "buffer.h" #include "array.h" +#include <assert.h> +#include <stdio.h> +#include <string.h> + static void configparser_push(config_t *ctx, data_config *dc, int isnew) { if (isnew) { dc->context_ndx = ctx->all_configs->used; @@ -24,6 +22,10 @@ static void configparser_push(config_t *ctx, data_config *dc, int isnew) { dc->parent = ctx->current; array_insert_unique(dc->parent->childs, (data_unset *)dc); } + if (ctx->configs_stack->used > 0 && ctx->current->context_ndx == 0) { + fprintf(stderr, "Cannot use conditionals inside a global { ... } block\n"); + exit(-1); + } array_insert_unique(ctx->configs_stack, (data_unset *)ctx->current); ctx->current = dc; } @@ -36,43 +38,25 @@ static data_config *configparser_pop(config_t *ctx) { /* return a copied variable */ static data_unset *configparser_get_variable(config_t *ctx, const buffer *key) { - if (strncmp(key->ptr, "env.", sizeof("env.") - 1) == 0) { - char *env; - - if (NULL != (env = getenv(key->ptr + 4))) { - data_string *ds; - ds = data_string_init(); - buffer_append_string(ds->value, env); - return (data_unset *)ds; - } - - fprintf(stderr, "Undefined env variable: %s\n", key->ptr + 4); - ctx->ok = 0; - - return NULL; - } else { - data_unset *du; - data_config *dc; + data_unset *du; + data_config *dc; #if 0 - fprintf(stderr, "get var %s\n", key->ptr); + fprintf(stderr, "get var %s\n", key->ptr); #endif - for (dc = ctx->current; dc; dc = dc->parent) { + for (dc = ctx->current; dc; dc = dc->parent) { #if 0 - fprintf(stderr, "get var on block: %s\n", dc->key->ptr); - array_print(dc->value, 0); + fprintf(stderr, "get var on block: %s\n", dc->key->ptr); + array_print(dc->value, 0); #endif - if (NULL != (du = array_get_element(dc->value, key->ptr))) { - return du->copy(du); - } + if (NULL != (du = array_get_element(dc->value, key->ptr))) { + return du->copy(du); } - fprintf(stderr, "Undefined config variable: %s\n", key->ptr); - ctx->ok = 0; - return NULL; } + return NULL; } -/* op1 is to be eat/return by this function, op1->key is not cared +/* op1 is to be eat/return by this function if success, op1->key is not cared op2 is left untouch, unreferenced */ data_unset *configparser_merge_data(data_unset *op1, const data_unset *op2) { @@ -89,8 +73,7 @@ data_unset *configparser_merge_data(data_unset *op1, const data_unset *op2) { op1->free(op1); return (data_unset *)ds; } else { - fprintf(stderr, "data type mismatch, cannot be merge\n"); - op1->free(op1); + fprintf(stderr, "data type mismatch, cannot merge\n"); return NULL; } } @@ -124,14 +107,14 @@ data_unset *configparser_merge_data(data_unset *op1, const data_unset *op2) { } -#line 128 "configparser.c" +#line 111 "configparser.c" /* Next is all token values, in a form suitable for use by makeheaders. ** This section will be null unless lemon is run with the -m switch. */ -/* +/* ** These constants (all generated automatically by the parser generator) ** specify the various kinds of tokens (terminals) that the parser -** understands. +** understands. ** ** Each symbol here is a terminal symbol in the grammar. */ @@ -148,7 +131,7 @@ data_unset *configparser_merge_data(data_unset *op1, const data_unset *op2) { ** and nonterminals. "int" is used otherwise. ** YYNOCODE is a number of type YYCODETYPE which corresponds ** to no legal terminal or nonterminal number. This -** number is used to fill in empty slots of the hash +** number is used to fill in empty slots of the hash ** table. ** YYFALLBACK If defined, this indicates that one or more tokens ** have fall-back values which should be used if the @@ -157,7 +140,7 @@ data_unset *configparser_merge_data(data_unset *op1, const data_unset *op2) { ** and nonterminal numbers. "unsigned char" is ** used if there are fewer than 250 rules and ** states combined. "int" is used otherwise. -** configparserTOKENTYPE is the data type used for minor tokens given +** configparserTOKENTYPE is the data type used for minor tokens given ** directly to the parser from the tokenizer. ** YYMINORTYPE is the data type used for all minor tokens. ** This is typically a union of many types, one of @@ -175,27 +158,27 @@ data_unset *configparser_merge_data(data_unset *op1, const data_unset *op2) { */ /* */ #define YYCODETYPE unsigned char -#define YYNOCODE 45 +#define YYNOCODE 48 #define YYACTIONTYPE unsigned char #define configparserTOKENTYPE buffer * typedef union { configparserTOKENTYPE yy0; - buffer * yy1; - config_cond_t yy29; - data_unset * yy43; - data_config * yy74; - array * yy78; - int yy89; + config_cond_t yy27; + array * yy40; + data_unset * yy41; + buffer * yy43; + data_config * yy78; + int yy95; } YYMINORTYPE; #define YYSTACKDEPTH 100 #define configparserARG_SDECL config_t *ctx; #define configparserARG_PDECL ,config_t *ctx #define configparserARG_FETCH config_t *ctx = yypParser->ctx #define configparserARG_STORE yypParser->ctx = ctx -#define YYNSTATE 56 -#define YYNRULE 36 -#define YYERRORSYMBOL 25 -#define YYERRSYMDT yy89 +#define YYNSTATE 63 +#define YYNRULE 40 +#define YYERRORSYMBOL 26 +#define YYERRSYMDT yy95 #define YY_NO_ACTION (YYNSTATE+YYNRULE+2) #define YY_ACCEPT_ACTION (YYNSTATE+YYNRULE+1) #define YY_ERROR_ACTION (YYNSTATE+YYNRULE) @@ -203,7 +186,7 @@ typedef union { /* Next are that tables used to determine what action to take based on the ** current state and lookahead token. These tables are used to implement ** functions that take a state number and lookahead value and return an -** action integer. +** action integer. ** ** Suppose the action integer is N. Then the action is determined as ** follows @@ -228,7 +211,7 @@ typedef union { ** If the index value yy_shift_ofst[S]+X is out of range or if the value ** yy_lookahead[yy_shift_ofst[S]+X] is not equal to X or if yy_shift_ofst[S] ** is equal to YY_SHIFT_USE_DFLT, it means that the action is not in the table -** and that yy_default[S] should be used instead. +** and that yy_default[S] should be used instead. ** ** The formula above is for computing the action when the lookahead is ** a terminal symbol. If the lookahead is a non-terminal (as occurs after @@ -248,62 +231,69 @@ typedef union { ** yy_default[] Default action for each state. */ static YYACTIONTYPE yy_action[] = { - /* 0 */ 2, 3, 4, 12, 13, 56, 14, 6, 38, 19, - /* 10 */ 79, 15, 27, 26, 35, 9, 28, 24, 21, 32, - /* 20 */ 40, 14, 93, 1, 19, 27, 26, 31, 52, 54, - /* 30 */ 24, 21, 32, 7, 39, 40, 48, 49, 50, 51, - /* 40 */ 27, 43, 18, 52, 54, 24, 21, 44, 27, 43, - /* 50 */ 84, 27, 43, 24, 21, 53, 24, 21, 55, 19, - /* 60 */ 40, 22, 23, 25, 27, 17, 27, 34, 33, 24, - /* 70 */ 21, 24, 21, 5, 27, 37, 10, 27, 47, 24, - /* 80 */ 21, 20, 24, 21, 80, 8, 24, 21, 16, 36, - /* 90 */ 18, 11, 9, 29, 30, 18, 89, 41, 45, 42, - /* 100 */ 46, + /* 0 */ 2, 3, 4, 5, 13, 14, 63, 15, 7, 45, + /* 10 */ 20, 88, 16, 46, 28, 49, 41, 10, 40, 25, + /* 20 */ 22, 50, 46, 8, 15, 104, 1, 20, 28, 18, + /* 30 */ 58, 60, 6, 25, 22, 40, 47, 62, 11, 46, + /* 40 */ 20, 9, 23, 24, 26, 29, 89, 58, 60, 10, + /* 50 */ 17, 38, 28, 27, 37, 19, 30, 25, 22, 34, + /* 60 */ 15, 100, 20, 20, 23, 24, 26, 12, 19, 31, + /* 70 */ 32, 40, 19, 44, 43, 46, 95, 35, 90, 89, + /* 80 */ 28, 49, 42, 58, 60, 25, 22, 59, 28, 27, + /* 90 */ 33, 48, 52, 25, 22, 34, 28, 49, 51, 28, + /* 100 */ 36, 25, 22, 61, 25, 22, 89, 28, 39, 89, + /* 110 */ 89, 89, 25, 22, 54, 55, 56, 57, 89, 28, + /* 120 */ 53, 21, 89, 89, 25, 22, 25, 22, }; static YYCODETYPE yy_lookahead[] = { - /* 0 */ 28, 29, 30, 31, 32, 0, 1, 42, 36, 4, - /* 10 */ 12, 39, 33, 34, 35, 43, 37, 38, 39, 40, - /* 20 */ 15, 1, 26, 27, 4, 33, 34, 35, 23, 24, - /* 30 */ 38, 39, 40, 12, 14, 15, 19, 20, 21, 22, - /* 40 */ 33, 34, 5, 23, 24, 38, 39, 40, 33, 34, - /* 50 */ 13, 33, 34, 38, 39, 40, 38, 39, 40, 4, - /* 60 */ 15, 6, 7, 8, 33, 34, 33, 34, 11, 38, - /* 70 */ 39, 38, 39, 1, 33, 34, 13, 33, 34, 38, - /* 80 */ 39, 33, 38, 39, 12, 36, 38, 39, 2, 3, - /* 90 */ 5, 27, 43, 9, 10, 5, 11, 16, 18, 17, - /* 100 */ 41, + /* 0 */ 29, 30, 31, 32, 33, 34, 0, 1, 44, 38, + /* 10 */ 4, 15, 41, 16, 35, 36, 45, 46, 12, 40, + /* 20 */ 41, 42, 16, 15, 1, 27, 28, 4, 35, 36, + /* 30 */ 24, 25, 1, 40, 41, 12, 17, 14, 13, 16, + /* 40 */ 4, 38, 6, 7, 8, 9, 15, 24, 25, 46, + /* 50 */ 2, 3, 35, 36, 37, 5, 39, 40, 41, 42, + /* 60 */ 1, 11, 4, 4, 6, 7, 8, 28, 5, 9, + /* 70 */ 10, 12, 5, 14, 28, 16, 13, 11, 13, 47, + /* 80 */ 35, 36, 13, 24, 25, 40, 41, 42, 35, 36, + /* 90 */ 37, 18, 43, 40, 41, 42, 35, 36, 19, 35, + /* 100 */ 36, 40, 41, 42, 40, 41, 47, 35, 36, 47, + /* 110 */ 47, 47, 40, 41, 20, 21, 22, 23, 47, 35, + /* 120 */ 36, 35, 47, 47, 40, 41, 40, 41, }; -#define YY_SHIFT_USE_DFLT (-3) +#define YY_SHIFT_USE_DFLT (-5) static signed char yy_shift_ofst[] = { - /* 0 */ -3, 5, -3, -3, 72, -2, 21, 45, -3, 63, - /* 10 */ -3, 20, -3, -3, -3, 86, 55, 90, 55, -3, - /* 20 */ -3, -3, -3, -3, -3, 55, 85, -3, 84, -3, - /* 30 */ 55, -3, 57, 55, 90, -3, 55, 90, -3, -3, - /* 40 */ 81, 82, 55, 90, 80, 17, 55, 37, -3, -3, - /* 50 */ -3, -3, 55, -3, 55, -3, + /* 0 */ -5, 6, -5, -5, -5, 31, -4, 8, -3, -5, + /* 10 */ 25, -5, 23, -5, -5, -5, 48, 58, 67, 58, + /* 20 */ -5, -5, -5, -5, -5, -5, 36, 50, -5, -5, + /* 30 */ 60, -5, 58, -5, 66, 58, 67, -5, 58, 67, + /* 40 */ 65, 69, -5, 59, -5, -5, 19, 73, 58, 67, + /* 50 */ 79, 94, 58, 63, -5, -5, -5, -5, 58, -5, + /* 60 */ 58, -5, -5, }; -#define YY_REDUCE_USE_DFLT (-36) +#define YY_REDUCE_USE_DFLT (-37) static signed char yy_reduce_ofst[] = { - /* 0 */ -4, -28, -36, -36, -35, -36, -36, 49, -36, -36, - /* 10 */ 64, -28, -36, -36, -36, -36, 31, -36, 48, -36, - /* 20 */ -36, -36, -36, -36, -36, -21, -36, -36, -36, -36, - /* 30 */ -8, -36, -36, 33, -36, -36, 41, -36, -36, -36, - /* 40 */ -36, -36, 7, -36, -36, 59, 44, -36, -36, -36, - /* 50 */ -36, -36, 15, -36, 18, -36, + /* 0 */ -2, -29, -37, -37, -37, -36, -37, -37, 3, -37, + /* 10 */ -37, 39, -29, -37, -37, -37, -37, -7, -37, 86, + /* 20 */ -37, -37, -37, -37, -37, -37, 17, -37, -37, -37, + /* 30 */ -37, -37, 53, -37, -37, 64, -37, -37, 72, -37, + /* 40 */ -37, -37, 46, -29, -37, -37, -37, -37, -21, -37, + /* 50 */ -37, 49, 84, -37, -37, -37, -37, -37, 45, -37, + /* 60 */ 61, -37, -37, }; static YYACTIONTYPE yy_default[] = { - /* 0 */ 58, 92, 57, 59, 92, 60, 92, 92, 81, 92, - /* 10 */ 58, 92, 61, 62, 63, 92, 92, 64, 92, 66, - /* 20 */ 67, 69, 70, 71, 72, 92, 77, 68, 92, 73, - /* 30 */ 75, 74, 92, 92, 78, 76, 92, 65, 82, 83, - /* 40 */ 92, 92, 92, 89, 92, 92, 92, 92, 85, 86, - /* 50 */ 87, 88, 92, 90, 92, 91, + /* 0 */ 65, 103, 64, 66, 67, 103, 68, 103, 103, 92, + /* 10 */ 103, 65, 103, 69, 70, 71, 103, 103, 72, 103, + /* 20 */ 74, 75, 77, 78, 79, 80, 103, 86, 76, 81, + /* 30 */ 103, 82, 84, 83, 103, 103, 87, 85, 103, 73, + /* 40 */ 103, 103, 65, 103, 91, 93, 103, 103, 103, 100, + /* 50 */ 103, 103, 103, 103, 96, 97, 98, 99, 103, 101, + /* 60 */ 103, 102, 94, }; #define YY_SZ_ACTTAB (sizeof(yy_action)/sizeof(yy_action[0])) /* The next table maps tokens into fallback tokens. If a construct ** like the following: -** +** ** %fallback ID X Y Z. ** ** appears in the grammer, then ID becomes a fallback token for X, Y, @@ -349,15 +339,15 @@ typedef struct yyParser yyParser; #ifndef NDEBUG #include <stdio.h> -static FILE *yyTraceFILE = 0; -static char *yyTracePrompt = 0; +static FILE *yyTraceFILE = NULL; +static char *yyTracePrompt = NULL; #endif /* NDEBUG */ #ifndef NDEBUG -/* +/* ** Turn parser tracing on by giving a stream to which to write the trace ** and a prompt to preface each trace message. Tracing is turned off -** by making either argument NULL +** by making either argument NULL ** ** Inputs: ** <ul> @@ -371,29 +361,32 @@ static char *yyTracePrompt = 0; ** Outputs: ** None. */ +#if 0 void configparserTrace(FILE *TraceFILE, char *zTracePrompt){ yyTraceFILE = TraceFILE; yyTracePrompt = zTracePrompt; if( yyTraceFILE==0 ) yyTracePrompt = 0; else if( yyTracePrompt==0 ) yyTraceFILE = 0; } +#endif #endif /* NDEBUG */ #ifndef NDEBUG /* For tracing shifts, the names of all terminals and nonterminals ** are required. The following table supplies these names */ -static const char *yyTokenName[] = { +static const char *yyTokenName[] = { "$", "EOL", "ASSIGN", "APPEND", "LKEY", "PLUS", "STRING", "INTEGER", "LPARAN", "RPARAN", "COMMA", "ARRAY_ASSIGN", - "ELSE", "LCURLY", "RCURLY", "DOLLAR", - "SRVVARNAME", "LBRACKET", "RBRACKET", "EQ", - "MATCH", "NE", "NOMATCH", "INCLUDE", - "INCLUDE_SHELL", "error", "input", "metalines", - "metaline", "varline", "condlines", "include", - "include_shell", "value", "expression", "aelement", - "condline", "aelements", "array", "key", - "stringop", "cond", "eols", "context", + "GLOBAL", "LCURLY", "RCURLY", "ELSE", + "DOLLAR", "SRVVARNAME", "LBRACKET", "RBRACKET", + "EQ", "MATCH", "NE", "NOMATCH", + "INCLUDE", "INCLUDE_SHELL", "error", "input", + "metalines", "metaline", "varline", "global", + "condlines", "include", "include_shell", "value", + "expression", "aelement", "condline", "aelements", + "array", "key", "stringop", "cond", + "eols", "globalstart", "context", }; #endif /* NDEBUG */ @@ -405,38 +398,42 @@ static const char *yyRuleName[] = { /* 1 */ "metalines ::= metalines metaline", /* 2 */ "metalines ::=", /* 3 */ "metaline ::= varline", - /* 4 */ "metaline ::= condlines EOL", - /* 5 */ "metaline ::= include", - /* 6 */ "metaline ::= include_shell", - /* 7 */ "metaline ::= EOL", - /* 8 */ "varline ::= key ASSIGN expression", - /* 9 */ "varline ::= key APPEND expression", - /* 10 */ "key ::= LKEY", - /* 11 */ "expression ::= expression PLUS value", - /* 12 */ "expression ::= value", - /* 13 */ "value ::= key", - /* 14 */ "value ::= STRING", - /* 15 */ "value ::= INTEGER", - /* 16 */ "value ::= array", - /* 17 */ "array ::= LPARAN aelements RPARAN", - /* 18 */ "aelements ::= aelements COMMA aelement", - /* 19 */ "aelements ::= aelements COMMA", - /* 20 */ "aelements ::= aelement", - /* 21 */ "aelement ::= expression", - /* 22 */ "aelement ::= stringop ARRAY_ASSIGN expression", - /* 23 */ "eols ::= EOL", - /* 24 */ "eols ::=", - /* 25 */ "condlines ::= condlines eols ELSE condline", - /* 26 */ "condlines ::= condline", - /* 27 */ "condline ::= context LCURLY metalines RCURLY", - /* 28 */ "context ::= DOLLAR SRVVARNAME LBRACKET stringop RBRACKET cond expression", - /* 29 */ "cond ::= EQ", - /* 30 */ "cond ::= MATCH", - /* 31 */ "cond ::= NE", - /* 32 */ "cond ::= NOMATCH", - /* 33 */ "stringop ::= expression", - /* 34 */ "include ::= INCLUDE stringop", - /* 35 */ "include_shell ::= INCLUDE_SHELL stringop", + /* 4 */ "metaline ::= global", + /* 5 */ "metaline ::= condlines EOL", + /* 6 */ "metaline ::= include", + /* 7 */ "metaline ::= include_shell", + /* 8 */ "metaline ::= EOL", + /* 9 */ "varline ::= key ASSIGN expression", + /* 10 */ "varline ::= key APPEND expression", + /* 11 */ "key ::= LKEY", + /* 12 */ "expression ::= expression PLUS value", + /* 13 */ "expression ::= value", + /* 14 */ "value ::= key", + /* 15 */ "value ::= STRING", + /* 16 */ "value ::= INTEGER", + /* 17 */ "value ::= array", + /* 18 */ "array ::= LPARAN RPARAN", + /* 19 */ "array ::= LPARAN aelements RPARAN", + /* 20 */ "aelements ::= aelements COMMA aelement", + /* 21 */ "aelements ::= aelements COMMA", + /* 22 */ "aelements ::= aelement", + /* 23 */ "aelement ::= expression", + /* 24 */ "aelement ::= stringop ARRAY_ASSIGN expression", + /* 25 */ "eols ::= EOL", + /* 26 */ "eols ::=", + /* 27 */ "globalstart ::= GLOBAL", + /* 28 */ "global ::= globalstart LCURLY metalines RCURLY", + /* 29 */ "condlines ::= condlines eols ELSE condline", + /* 30 */ "condlines ::= condline", + /* 31 */ "condline ::= context LCURLY metalines RCURLY", + /* 32 */ "context ::= DOLLAR SRVVARNAME LBRACKET stringop RBRACKET cond expression", + /* 33 */ "cond ::= EQ", + /* 34 */ "cond ::= MATCH", + /* 35 */ "cond ::= NE", + /* 36 */ "cond ::= NOMATCH", + /* 37 */ "stringop ::= expression", + /* 38 */ "include ::= INCLUDE stringop", + /* 39 */ "include_shell ::= INCLUDE_SHELL stringop", }; #endif /* NDEBUG */ @@ -444,9 +441,10 @@ static const char *yyRuleName[] = { ** This function returns the symbolic name associated with a token ** value. */ +#if 0 const char *configparserTokenName(int tokenType){ #ifndef NDEBUG - if( tokenType>0 && tokenType<(sizeof(yyTokenName)/sizeof(yyTokenName[0])) ){ + if( tokenType>0 && (size_t)tokenType<(sizeof(yyTokenName)/sizeof(yyTokenName[0])) ){ return yyTokenName[tokenType]; }else{ return "Unknown"; @@ -455,8 +453,9 @@ const char *configparserTokenName(int tokenType){ return ""; #endif } +#endif -/* +/* ** This function allocates a new parser. ** The only argument is a pointer to a function which works like ** malloc. @@ -487,7 +486,7 @@ static void yy_destructor(YYCODETYPE yymajor, YYMINORTYPE *yypminor){ /* Here is inserted the actions which take place when a ** terminal or non-terminal is destroyed. This can happen ** when the symbol is popped from the stack during a - ** reduce or during error processing or when a parser is + ** reduce or during error processing or when a parser is ** being destroyed before it is finished parsing. ** ** Note: during a reduce, the only symbols destroyed are those @@ -518,43 +517,44 @@ static void yy_destructor(YYCODETYPE yymajor, YYMINORTYPE *yypminor){ case 22: case 23: case 24: -#line 159 "./configparser.y" + case 25: +#line 144 "../../src/configparser.y" { buffer_free((yypminor->yy0)); } #line 523 "configparser.c" break; - case 33: -#line 150 "./configparser.y" -{ (yypminor->yy43)->free((yypminor->yy43)); } + case 35: +#line 135 "../../src/configparser.y" +{ (yypminor->yy41)->free((yypminor->yy41)); } #line 528 "configparser.c" break; - case 34: -#line 151 "./configparser.y" -{ (yypminor->yy43)->free((yypminor->yy43)); } + case 36: +#line 136 "../../src/configparser.y" +{ (yypminor->yy41)->free((yypminor->yy41)); } #line 533 "configparser.c" break; - case 35: -#line 152 "./configparser.y" -{ (yypminor->yy43)->free((yypminor->yy43)); } + case 37: +#line 137 "../../src/configparser.y" +{ (yypminor->yy41)->free((yypminor->yy41)); } #line 538 "configparser.c" break; - case 37: -#line 153 "./configparser.y" -{ array_free((yypminor->yy78)); } + case 39: +#line 138 "../../src/configparser.y" +{ array_free((yypminor->yy40)); } #line 543 "configparser.c" break; - case 38: -#line 154 "./configparser.y" -{ array_free((yypminor->yy78)); } + case 40: +#line 139 "../../src/configparser.y" +{ array_free((yypminor->yy40)); } #line 548 "configparser.c" break; - case 39: -#line 155 "./configparser.y" -{ buffer_free((yypminor->yy1)); } + case 41: +#line 140 "../../src/configparser.y" +{ buffer_free((yypminor->yy43)); } #line 553 "configparser.c" break; - case 40: -#line 156 "./configparser.y" -{ buffer_free((yypminor->yy1)); } + case 42: +#line 141 "../../src/configparser.y" +{ buffer_free((yypminor->yy43)); } #line 558 "configparser.c" break; default: break; /* If no destructor action specified: do nothing */ @@ -587,7 +587,7 @@ static int yy_pop_parser_stack(yyParser *pParser){ return yymajor; } -/* +/* ** Deallocate and destroy a parser. Destructors are all called for ** all stack elements before shutting the parser down. ** @@ -604,7 +604,7 @@ void configparserFree( void (*freeProc)(void*) /* Function used to reclaim memory */ ){ yyParser *pParser = (yyParser*)p; - if( pParser==0 ) return; + if( pParser==NULL ) return; while( pParser->yyidx>=0 ) yy_pop_parser_stack(pParser); (*freeProc)((void*)pParser); } @@ -623,7 +623,7 @@ static int yy_find_shift_action( ){ int i; int stateno = pParser->yystack[pParser->yyidx].stateno; - + /* if( pParser->yyidx<0 ) return YY_NO_ACTION; */ i = yy_shift_ofst[stateno]; if( i==YY_SHIFT_USE_DFLT ){ @@ -633,7 +633,7 @@ static int yy_find_shift_action( return YY_NO_ACTION; } i += iLookAhead; - if( i<0 || i>=YY_SZ_ACTTAB || yy_lookahead[i]!=iLookAhead ){ + if( i<0 || (size_t)i>=YY_SZ_ACTTAB || yy_lookahead[i]!=iLookAhead ){ #ifdef YYFALLBACK int iFallback; /* Fallback token */ if( iLookAhead<sizeof(yyFallback)/sizeof(yyFallback[0]) @@ -667,7 +667,7 @@ static int yy_find_reduce_action( ){ int i; int stateno = pParser->yystack[pParser->yyidx].stateno; - + i = yy_reduce_ofst[stateno]; if( i==YY_REDUCE_USE_DFLT ){ return yy_default[stateno]; @@ -676,7 +676,7 @@ static int yy_find_reduce_action( return YY_NO_ACTION; } i += iLookAhead; - if( i<0 || i>=YY_SZ_ACTTAB || yy_lookahead[i]!=iLookAhead ){ + if( i<0 || (size_t)i>=YY_SZ_ACTTAB || yy_lookahead[i]!=iLookAhead ){ return yy_default[stateno]; }else{ return yy_action[i]; @@ -731,42 +731,46 @@ static struct { YYCODETYPE lhs; /* Symbol on the left-hand side of the rule */ unsigned char nrhs; /* Number of right-hand side symbols in the rule */ } yyRuleInfo[] = { - { 26, 1 }, - { 27, 2 }, - { 27, 0 }, - { 28, 1 }, + { 27, 1 }, { 28, 2 }, - { 28, 1 }, - { 28, 1 }, - { 28, 1 }, - { 29, 3 }, - { 29, 3 }, + { 28, 0 }, + { 29, 1 }, + { 29, 1 }, + { 29, 2 }, + { 29, 1 }, + { 29, 1 }, + { 29, 1 }, + { 30, 3 }, + { 30, 3 }, + { 41, 1 }, + { 36, 3 }, + { 36, 1 }, + { 35, 1 }, + { 35, 1 }, + { 35, 1 }, + { 35, 1 }, + { 40, 2 }, + { 40, 3 }, + { 39, 3 }, + { 39, 2 }, { 39, 1 }, - { 34, 3 }, - { 34, 1 }, - { 33, 1 }, - { 33, 1 }, - { 33, 1 }, - { 33, 1 }, - { 38, 3 }, - { 37, 3 }, - { 37, 2 }, { 37, 1 }, - { 35, 1 }, - { 35, 3 }, + { 37, 3 }, + { 44, 1 }, + { 44, 0 }, + { 45, 1 }, + { 31, 4 }, + { 32, 4 }, + { 32, 1 }, + { 38, 4 }, + { 46, 7 }, + { 43, 1 }, + { 43, 1 }, + { 43, 1 }, + { 43, 1 }, { 42, 1 }, - { 42, 0 }, - { 30, 4 }, - { 30, 1 }, - { 36, 4 }, - { 43, 7 }, - { 41, 1 }, - { 41, 1 }, - { 41, 1 }, - { 41, 1 }, - { 40, 1 }, - { 31, 2 }, - { 32, 2 }, + { 33, 2 }, + { 34, 2 }, }; static void yy_accept(yyParser*); /* Forward Declaration */ @@ -787,8 +791,8 @@ static void yy_reduce( configparserARG_FETCH; yymsp = &yypParser->yystack[yypParser->yyidx]; #ifndef NDEBUG - if( yyTraceFILE && yyruleno>=0 - && yyruleno<sizeof(yyRuleName)/sizeof(yyRuleName[0]) ){ + if( yyTraceFILE && yyruleno>=0 + && (size_t)yyruleno<sizeof(yyRuleName)/sizeof(yyRuleName[0]) ){ fprintf(yyTraceFILE, "%sReduce [%s].\n", yyTracePrompt, yyRuleName[yyruleno]); } @@ -816,287 +820,359 @@ static void yy_reduce( /* No destructor defined for varline */ break; case 4: -#line 133 "./configparser.y" -{ yymsp[-1].minor.yy74 = NULL; } -#line 821 "configparser.c" - yy_destructor(1,&yymsp[0].minor); + /* No destructor defined for global */ break; case 5: - /* No destructor defined for include */ +#line 117 "../../src/configparser.y" +{ yymsp[-1].minor.yy78 = NULL; } +#line 828 "configparser.c" + yy_destructor(1,&yymsp[0].minor); break; case 6: - /* No destructor defined for include_shell */ + /* No destructor defined for include */ break; case 7: - yy_destructor(1,&yymsp[0].minor); + /* No destructor defined for include_shell */ break; case 8: -#line 161 "./configparser.y" + yy_destructor(1,&yymsp[0].minor); + break; + case 9: +#line 146 "../../src/configparser.y" { - buffer_copy_string_buffer(yymsp[0].minor.yy43->key, yymsp[-2].minor.yy1); - if (NULL == array_get_element(ctx->current->value, yymsp[0].minor.yy43->key->ptr)) { - array_insert_unique(ctx->current->value, yymsp[0].minor.yy43); - yymsp[0].minor.yy43 = NULL; - } else { - fprintf(stderr, "Duplicate config variable in conditional %d %s: %s\n", - ctx->current->context_ndx, - ctx->current->key->ptr, yymsp[0].minor.yy43->key->ptr); - ctx->ok = 0; - yymsp[0].minor.yy43->free(yymsp[0].minor.yy43); - yymsp[0].minor.yy43 = NULL; + if (ctx->ok) { + buffer_copy_string_buffer(yymsp[0].minor.yy41->key, yymsp[-2].minor.yy43); + if (strncmp(yymsp[-2].minor.yy43->ptr, "env.", sizeof("env.") - 1) == 0) { + fprintf(stderr, "Setting env variable is not supported in conditional %d %s: %s\n", + ctx->current->context_ndx, + ctx->current->key->ptr, yymsp[-2].minor.yy43->ptr); + ctx->ok = 0; + } else if (NULL == array_get_element(ctx->current->value, yymsp[0].minor.yy41->key->ptr)) { + array_insert_unique(ctx->current->value, yymsp[0].minor.yy41); + yymsp[0].minor.yy41 = NULL; + } else { + fprintf(stderr, "Duplicate config variable in conditional %d %s: %s\n", + ctx->current->context_ndx, + ctx->current->key->ptr, yymsp[0].minor.yy41->key->ptr); + ctx->ok = 0; + yymsp[0].minor.yy41->free(yymsp[0].minor.yy41); + yymsp[0].minor.yy41 = NULL; + } } - buffer_free(yymsp[-2].minor.yy1); - yymsp[-2].minor.yy1 = NULL; + buffer_free(yymsp[-2].minor.yy43); + yymsp[-2].minor.yy43 = NULL; } -#line 851 "configparser.c" +#line 865 "configparser.c" yy_destructor(2,&yymsp[-1].minor); break; - case 9: -#line 178 "./configparser.y" + case 10: +#line 170 "../../src/configparser.y" { array *vars = ctx->current->value; data_unset *du; - if (NULL != (du = array_get_element(vars, yymsp[-2].minor.yy1->ptr))) { + if (strncmp(yymsp[-2].minor.yy43->ptr, "env.", sizeof("env.") - 1) == 0) { + fprintf(stderr, "Appending env variable is not supported in conditional %d %s: %s\n", + ctx->current->context_ndx, + ctx->current->key->ptr, yymsp[-2].minor.yy43->ptr); + ctx->ok = 0; + } else if (NULL != (du = array_get_element(vars, yymsp[-2].minor.yy43->ptr))) { /* exists in current block */ - du = configparser_merge_data(du, yymsp[0].minor.yy43); + du = configparser_merge_data(du, yymsp[0].minor.yy41); if (NULL == du) { ctx->ok = 0; } else { - buffer_copy_string_buffer(du->key, yymsp[-2].minor.yy1); + buffer_copy_string_buffer(du->key, yymsp[-2].minor.yy43); array_replace(vars, du); } - } else if (NULL != (du = configparser_get_variable(ctx, yymsp[-2].minor.yy1))) { - du = configparser_merge_data(du, yymsp[0].minor.yy43); + yymsp[0].minor.yy41->free(yymsp[0].minor.yy41); + } else if (NULL != (du = configparser_get_variable(ctx, yymsp[-2].minor.yy43))) { + du = configparser_merge_data(du, yymsp[0].minor.yy41); if (NULL == du) { ctx->ok = 0; } else { - buffer_copy_string_buffer(du->key, yymsp[-2].minor.yy1); + buffer_copy_string_buffer(du->key, yymsp[-2].minor.yy43); array_insert_unique(ctx->current->value, du); } + yymsp[0].minor.yy41->free(yymsp[0].minor.yy41); } else { - fprintf(stderr, "Undefined config variable in conditional %d %s: %s\n", - ctx->current->context_ndx, - ctx->current->key->ptr, yymsp[-2].minor.yy1->ptr); - ctx->ok = 0; + buffer_copy_string_buffer(yymsp[0].minor.yy41->key, yymsp[-2].minor.yy43); + array_insert_unique(ctx->current->value, yymsp[0].minor.yy41); } - buffer_free(yymsp[-2].minor.yy1); - yymsp[-2].minor.yy1 = NULL; - yymsp[0].minor.yy43->free(yymsp[0].minor.yy43); - yymsp[0].minor.yy43 = NULL; + buffer_free(yymsp[-2].minor.yy43); + yymsp[-2].minor.yy43 = NULL; + yymsp[0].minor.yy41 = NULL; } -#line 890 "configparser.c" +#line 908 "configparser.c" yy_destructor(3,&yymsp[-1].minor); break; - case 10: -#line 213 "./configparser.y" + case 11: +#line 209 "../../src/configparser.y" { if (strchr(yymsp[0].minor.yy0->ptr, '.') == NULL) { - yygotominor.yy1 = buffer_init_string("var."); - buffer_append_string_buffer(yygotominor.yy1, yymsp[0].minor.yy0); + yygotominor.yy43 = buffer_init_string("var."); + buffer_append_string_buffer(yygotominor.yy43, yymsp[0].minor.yy0); buffer_free(yymsp[0].minor.yy0); yymsp[0].minor.yy0 = NULL; } else { - yygotominor.yy1 = yymsp[0].minor.yy0; + yygotominor.yy43 = yymsp[0].minor.yy0; yymsp[0].minor.yy0 = NULL; } } -#line 906 "configparser.c" +#line 924 "configparser.c" break; - case 11: -#line 225 "./configparser.y" + case 12: +#line 221 "../../src/configparser.y" { - yygotominor.yy43 = configparser_merge_data(yymsp[-2].minor.yy43, yymsp[0].minor.yy43); - if (NULL == yygotominor.yy43) { + yygotominor.yy41 = configparser_merge_data(yymsp[-2].minor.yy41, yymsp[0].minor.yy41); + if (NULL == yygotominor.yy41) { ctx->ok = 0; } - yymsp[-2].minor.yy43 = NULL; - yymsp[0].minor.yy43->free(yymsp[0].minor.yy43); - yymsp[0].minor.yy43 = NULL; + yymsp[-2].minor.yy41 = NULL; + yymsp[0].minor.yy41->free(yymsp[0].minor.yy41); + yymsp[0].minor.yy41 = NULL; } -#line 919 "configparser.c" +#line 937 "configparser.c" yy_destructor(5,&yymsp[-1].minor); break; - case 12: -#line 235 "./configparser.y" + case 13: +#line 231 "../../src/configparser.y" { - yygotominor.yy43 = yymsp[0].minor.yy43; - yymsp[0].minor.yy43 = NULL; + yygotominor.yy41 = yymsp[0].minor.yy41; + yymsp[0].minor.yy41 = NULL; } -#line 928 "configparser.c" +#line 946 "configparser.c" break; - case 13: -#line 240 "./configparser.y" + case 14: +#line 236 "../../src/configparser.y" { - yygotominor.yy43 = configparser_get_variable(ctx, yymsp[0].minor.yy1); - if (!yygotominor.yy43) { + yygotominor.yy41 = NULL; + if (strncmp(yymsp[0].minor.yy43->ptr, "env.", sizeof("env.") - 1) == 0) { + char *env; + + if (NULL != (env = getenv(yymsp[0].minor.yy43->ptr + 4))) { + data_string *ds; + ds = data_string_init(); + buffer_append_string(ds->value, env); + yygotominor.yy41 = (data_unset *)ds; + } + else { + fprintf(stderr, "Undefined env variable: %s\n", yymsp[0].minor.yy43->ptr + 4); + ctx->ok = 0; + } + } else if (NULL == (yygotominor.yy41 = configparser_get_variable(ctx, yymsp[0].minor.yy43))) { + fprintf(stderr, "Undefined config variable: %s\n", yymsp[0].minor.yy43->ptr); + ctx->ok = 0; + } + if (!yygotominor.yy41) { /* make a dummy so it won't crash */ - yygotominor.yy43 = (data_unset *)data_string_init(); + yygotominor.yy41 = (data_unset *)data_string_init(); } - buffer_free(yymsp[0].minor.yy1); - yymsp[0].minor.yy1 = NULL; + buffer_free(yymsp[0].minor.yy43); + yymsp[0].minor.yy43 = NULL; } -#line 941 "configparser.c" +#line 976 "configparser.c" break; - case 14: -#line 250 "./configparser.y" + case 15: +#line 263 "../../src/configparser.y" { - yygotominor.yy43 = (data_unset *)data_string_init(); - buffer_copy_string_buffer(((data_string *)(yygotominor.yy43))->value, yymsp[0].minor.yy0); + yygotominor.yy41 = (data_unset *)data_string_init(); + buffer_copy_string_buffer(((data_string *)(yygotominor.yy41))->value, yymsp[0].minor.yy0); buffer_free(yymsp[0].minor.yy0); yymsp[0].minor.yy0 = NULL; } -#line 951 "configparser.c" +#line 986 "configparser.c" break; - case 15: -#line 257 "./configparser.y" + case 16: +#line 270 "../../src/configparser.y" { - yygotominor.yy43 = (data_unset *)data_integer_init(); - ((data_integer *)(yygotominor.yy43))->value = strtol(yymsp[0].minor.yy0->ptr, NULL, 10); + yygotominor.yy41 = (data_unset *)data_integer_init(); + ((data_integer *)(yygotominor.yy41))->value = strtol(yymsp[0].minor.yy0->ptr, NULL, 10); buffer_free(yymsp[0].minor.yy0); yymsp[0].minor.yy0 = NULL; } -#line 961 "configparser.c" +#line 996 "configparser.c" break; - case 16: -#line 263 "./configparser.y" + case 17: +#line 276 "../../src/configparser.y" { - yygotominor.yy43 = (data_unset *)data_array_init(); - array_free(((data_array *)(yygotominor.yy43))->value); - ((data_array *)(yygotominor.yy43))->value = yymsp[0].minor.yy78; - yymsp[0].minor.yy78 = NULL; + yygotominor.yy41 = (data_unset *)data_array_init(); + array_free(((data_array *)(yygotominor.yy41))->value); + ((data_array *)(yygotominor.yy41))->value = yymsp[0].minor.yy40; + yymsp[0].minor.yy40 = NULL; } -#line 971 "configparser.c" +#line 1006 "configparser.c" break; - case 17: -#line 269 "./configparser.y" + case 18: +#line 282 "../../src/configparser.y" { - yygotominor.yy78 = yymsp[-1].minor.yy78; - yymsp[-1].minor.yy78 = NULL; + yygotominor.yy40 = array_init(); } -#line 979 "configparser.c" +#line 1013 "configparser.c" + yy_destructor(8,&yymsp[-1].minor); + yy_destructor(9,&yymsp[0].minor); + break; + case 19: +#line 285 "../../src/configparser.y" +{ + yygotominor.yy40 = yymsp[-1].minor.yy40; + yymsp[-1].minor.yy40 = NULL; +} +#line 1023 "configparser.c" yy_destructor(8,&yymsp[-2].minor); yy_destructor(9,&yymsp[0].minor); break; - case 18: -#line 274 "./configparser.y" + case 20: +#line 290 "../../src/configparser.y" { - if (buffer_is_empty(yymsp[0].minor.yy43->key) || - NULL == array_get_element(yymsp[-2].minor.yy78, yymsp[0].minor.yy43->key->ptr)) { - array_insert_unique(yymsp[-2].minor.yy78, yymsp[0].minor.yy43); - yymsp[0].minor.yy43 = NULL; + if (buffer_is_empty(yymsp[0].minor.yy41->key) || + NULL == array_get_element(yymsp[-2].minor.yy40, yymsp[0].minor.yy41->key->ptr)) { + array_insert_unique(yymsp[-2].minor.yy40, yymsp[0].minor.yy41); + yymsp[0].minor.yy41 = NULL; } else { - fprintf(stderr, "Duplicate array-key: %s\n", - yymsp[0].minor.yy43->key->ptr); + fprintf(stderr, "Duplicate array-key: %s\n", + yymsp[0].minor.yy41->key->ptr); ctx->ok = 0; - yymsp[0].minor.yy43->free(yymsp[0].minor.yy43); - yymsp[0].minor.yy43 = NULL; + yymsp[0].minor.yy41->free(yymsp[0].minor.yy41); + yymsp[0].minor.yy41 = NULL; } - - yygotominor.yy78 = yymsp[-2].minor.yy78; - yymsp[-2].minor.yy78 = NULL; + + yygotominor.yy40 = yymsp[-2].minor.yy40; + yymsp[-2].minor.yy40 = NULL; } -#line 1001 "configparser.c" +#line 1045 "configparser.c" yy_destructor(10,&yymsp[-1].minor); break; - case 19: -#line 291 "./configparser.y" + case 21: +#line 307 "../../src/configparser.y" { - yygotominor.yy78 = yymsp[-1].minor.yy78; - yymsp[-1].minor.yy78 = NULL; + yygotominor.yy40 = yymsp[-1].minor.yy40; + yymsp[-1].minor.yy40 = NULL; } -#line 1010 "configparser.c" +#line 1054 "configparser.c" yy_destructor(10,&yymsp[0].minor); break; - case 20: -#line 296 "./configparser.y" + case 22: +#line 312 "../../src/configparser.y" { - yygotominor.yy78 = array_init(); - array_insert_unique(yygotominor.yy78, yymsp[0].minor.yy43); - yymsp[0].minor.yy43 = NULL; + yygotominor.yy40 = array_init(); + array_insert_unique(yygotominor.yy40, yymsp[0].minor.yy41); + yymsp[0].minor.yy41 = NULL; } -#line 1020 "configparser.c" +#line 1064 "configparser.c" break; - case 21: -#line 302 "./configparser.y" + case 23: +#line 318 "../../src/configparser.y" { - yygotominor.yy43 = yymsp[0].minor.yy43; - yymsp[0].minor.yy43 = NULL; + yygotominor.yy41 = yymsp[0].minor.yy41; + yymsp[0].minor.yy41 = NULL; } -#line 1028 "configparser.c" +#line 1072 "configparser.c" break; - case 22: -#line 306 "./configparser.y" + case 24: +#line 322 "../../src/configparser.y" { - buffer_copy_string_buffer(yymsp[0].minor.yy43->key, yymsp[-2].minor.yy1); - buffer_free(yymsp[-2].minor.yy1); - yymsp[-2].minor.yy1 = NULL; - - yygotominor.yy43 = yymsp[0].minor.yy43; - yymsp[0].minor.yy43 = NULL; + buffer_copy_string_buffer(yymsp[0].minor.yy41->key, yymsp[-2].minor.yy43); + buffer_free(yymsp[-2].minor.yy43); + yymsp[-2].minor.yy43 = NULL; + + yygotominor.yy41 = yymsp[0].minor.yy41; + yymsp[0].minor.yy41 = NULL; } -#line 1040 "configparser.c" +#line 1084 "configparser.c" yy_destructor(11,&yymsp[-1].minor); break; - case 23: + case 25: yy_destructor(1,&yymsp[0].minor); break; - case 24: + case 26: break; - case 25: -#line 318 "./configparser.y" + case 27: +#line 334 "../../src/configparser.y" { - assert(yymsp[-3].minor.yy74->context_ndx < yymsp[0].minor.yy74->context_ndx); - yymsp[0].minor.yy74->prev = yymsp[-3].minor.yy74; - yymsp[-3].minor.yy74->next = yymsp[0].minor.yy74; - yygotominor.yy74 = yymsp[0].minor.yy74; - yymsp[-3].minor.yy74 = NULL; - yymsp[0].minor.yy74 = NULL; + data_config *dc; + dc = (data_config *)array_get_element(ctx->srv->config_context, "global"); + assert(dc); + configparser_push(ctx, dc, 0); } -#line 1058 "configparser.c" +#line 1100 "configparser.c" + yy_destructor(12,&yymsp[0].minor); + break; + case 28: +#line 341 "../../src/configparser.y" +{ + data_config *cur; + + cur = ctx->current; + configparser_pop(ctx); + + assert(cur && ctx->current); + + yygotominor.yy78 = cur; +} +#line 1115 "configparser.c" + /* No destructor defined for globalstart */ + yy_destructor(13,&yymsp[-2].minor); + /* No destructor defined for metalines */ + yy_destructor(14,&yymsp[0].minor); + break; + case 29: +#line 352 "../../src/configparser.y" +{ + if (yymsp[-3].minor.yy78->context_ndx >= yymsp[0].minor.yy78->context_ndx) { + fprintf(stderr, "unreachable else condition\n"); + ctx->ok = 0; + } + yymsp[0].minor.yy78->prev = yymsp[-3].minor.yy78; + yymsp[-3].minor.yy78->next = yymsp[0].minor.yy78; + yygotominor.yy78 = yymsp[0].minor.yy78; + yymsp[-3].minor.yy78 = NULL; + yymsp[0].minor.yy78 = NULL; +} +#line 1134 "configparser.c" /* No destructor defined for eols */ - yy_destructor(12,&yymsp[-1].minor); + yy_destructor(15,&yymsp[-1].minor); break; - case 26: -#line 327 "./configparser.y" + case 30: +#line 364 "../../src/configparser.y" { - yygotominor.yy74 = yymsp[0].minor.yy74; - yymsp[0].minor.yy74 = NULL; + yygotominor.yy78 = yymsp[0].minor.yy78; + yymsp[0].minor.yy78 = NULL; } -#line 1068 "configparser.c" +#line 1144 "configparser.c" break; - case 27: -#line 332 "./configparser.y" + case 31: +#line 369 "../../src/configparser.y" { data_config *cur; - + cur = ctx->current; configparser_pop(ctx); assert(cur && ctx->current); - yygotominor.yy74 = cur; + yygotominor.yy78 = cur; } -#line 1082 "configparser.c" +#line 1158 "configparser.c" /* No destructor defined for context */ yy_destructor(13,&yymsp[-2].minor); /* No destructor defined for metalines */ yy_destructor(14,&yymsp[0].minor); break; - case 28: -#line 343 "./configparser.y" + case 32: +#line 380 "../../src/configparser.y" { data_config *dc; buffer *b, *rvalue, *op; - if (ctx->ok && yymsp[0].minor.yy43->type != TYPE_STRING) { + if (ctx->ok && yymsp[0].minor.yy41->type != TYPE_STRING) { fprintf(stderr, "rvalue must be string"); ctx->ok = 0; } - switch(yymsp[-1].minor.yy29) { + switch(yymsp[-1].minor.yy27) { case CONFIG_COND_NE: op = buffer_init_string("!="); break; @@ -1118,11 +1194,11 @@ static void yy_reduce( buffer_copy_string_buffer(b, ctx->current->key); buffer_append_string(b, "/"); buffer_append_string_buffer(b, yymsp[-5].minor.yy0); - buffer_append_string_buffer(b, yymsp[-3].minor.yy1); + buffer_append_string_buffer(b, yymsp[-3].minor.yy43); buffer_append_string_buffer(b, op); - rvalue = ((data_string*)yymsp[0].minor.yy43)->value; + rvalue = ((data_string*)yymsp[0].minor.yy41)->value; buffer_append_string_buffer(b, rvalue); - + if (NULL != (dc = (data_config *)array_get_element(ctx->all_configs, b->ptr))) { configparser_push(ctx, dc, 0); } else { @@ -1135,23 +1211,30 @@ static void yy_reduce( { COMP_HTTP_URL, CONST_STR_LEN("HTTP[\"url\"]" ) }, { COMP_HTTP_HOST, CONST_STR_LEN("HTTP[\"host\"]" ) }, { COMP_HTTP_REFERER, CONST_STR_LEN("HTTP[\"referer\"]" ) }, - { COMP_HTTP_USERAGENT, CONST_STR_LEN("HTTP[\"useragent\"]" ) }, + { COMP_HTTP_USER_AGENT, CONST_STR_LEN("HTTP[\"useragent\"]" ) }, + { COMP_HTTP_USER_AGENT, CONST_STR_LEN("HTTP[\"user-agent\"]" ) }, + { COMP_HTTP_LANGUAGE, CONST_STR_LEN("HTTP[\"language\"]" ) }, { COMP_HTTP_COOKIE, CONST_STR_LEN("HTTP[\"cookie\"]" ) }, - { COMP_HTTP_REMOTEIP, CONST_STR_LEN("HTTP[\"remoteip\"]" ) }, + { COMP_HTTP_REMOTE_IP, CONST_STR_LEN("HTTP[\"remoteip\"]" ) }, + { COMP_HTTP_REMOTE_IP, CONST_STR_LEN("HTTP[\"remote-ip\"]" ) }, + { COMP_HTTP_QUERY_STRING, CONST_STR_LEN("HTTP[\"querystring\"]") }, + { COMP_HTTP_QUERY_STRING, CONST_STR_LEN("HTTP[\"query-string\"]") }, + { COMP_HTTP_REQUEST_METHOD, CONST_STR_LEN("HTTP[\"request-method\"]") }, + { COMP_HTTP_SCHEME, CONST_STR_LEN("HTTP[\"scheme\"]" ) }, { COMP_UNSET, NULL, 0 }, }; size_t i; dc = data_config_init(); - + buffer_copy_string_buffer(dc->key, b); buffer_copy_string_buffer(dc->op, op); buffer_copy_string_buffer(dc->comp_key, yymsp[-5].minor.yy0); buffer_append_string_len(dc->comp_key, CONST_STR_LEN("[\"")); - buffer_append_string_buffer(dc->comp_key, yymsp[-3].minor.yy1); + buffer_append_string_buffer(dc->comp_key, yymsp[-3].minor.yy43); buffer_append_string_len(dc->comp_key, CONST_STR_LEN("\"]")); - dc->cond = yymsp[-1].minor.yy29; - + dc->cond = yymsp[-1].minor.yy27; + for (i = 0; comps[i].comp_key; i ++) { if (buffer_is_equal_string( dc->comp_key, comps[i].comp_key, comps[i].len)) { @@ -1164,7 +1247,7 @@ static void yy_reduce( ctx->ok = 0; } - switch(yymsp[-1].minor.yy29) { + switch(yymsp[-1].minor.yy27) { case CONFIG_COND_NE: case CONFIG_COND_EQ: dc->string = buffer_init_buffer(rvalue); @@ -1173,42 +1256,50 @@ static void yy_reduce( case CONFIG_COND_MATCH: { #ifdef HAVE_PCRE_H const char *errptr; - int erroff; - - if (NULL == (dc->regex = + int erroff, captures; + + if (NULL == (dc->regex = pcre_compile(rvalue->ptr, 0, &errptr, &erroff, NULL))) { dc->string = buffer_init_string(errptr); dc->cond = CONFIG_COND_UNSET; - fprintf(stderr, "parsing regex failed: %s -> %s at offset %d\n", + fprintf(stderr, "parsing regex failed: %s -> %s at offset %d\n", rvalue->ptr, errptr, erroff); ctx->ok = 0; } else if (NULL == (dc->regex_study = - pcre_study(dc->regex, 0, &errptr)) && + pcre_study(dc->regex, 0, &errptr)) && errptr != NULL) { - fprintf(stderr, "studying regex failed: %s -> %s\n", + fprintf(stderr, "studying regex failed: %s -> %s\n", rvalue->ptr, errptr); ctx->ok = 0; + } else if (0 != (pcre_fullinfo(dc->regex, dc->regex_study, PCRE_INFO_CAPTURECOUNT, &captures))) { + fprintf(stderr, "getting capture count for regex failed: %s\n", + rvalue->ptr); + ctx->ok = 0; + } else if (captures > 9) { + fprintf(stderr, "Too many captures in regex, use (?:...) instead of (...): %s\n", + rvalue->ptr); + ctx->ok = 0; } else { dc->string = buffer_init_buffer(rvalue); } #else fprintf(stderr, "can't handle '$%s[%s] =~ ...' as you compiled without pcre support. \n" - "(perhaps just a missing pcre-devel package ?) \n", - yymsp[-5].minor.yy0->ptr, yymsp[-3].minor.yy1->ptr); + "(perhaps just a missing pcre-devel package ?) \n", + yymsp[-5].minor.yy0->ptr, yymsp[-3].minor.yy43->ptr); ctx->ok = 0; #endif break; } default: - fprintf(stderr, "unknown condition for $%s[%s]\n", - yymsp[-5].minor.yy0->ptr, yymsp[-3].minor.yy1->ptr); + fprintf(stderr, "unknown condition for $%s[%s]\n", + yymsp[-5].minor.yy0->ptr, yymsp[-3].minor.yy43->ptr); ctx->ok = 0; break; } - + configparser_push(ctx, dc, 1); } @@ -1216,95 +1307,95 @@ static void yy_reduce( buffer_free(op); buffer_free(yymsp[-5].minor.yy0); yymsp[-5].minor.yy0 = NULL; - buffer_free(yymsp[-3].minor.yy1); - yymsp[-3].minor.yy1 = NULL; - yymsp[0].minor.yy43->free(yymsp[0].minor.yy43); - yymsp[0].minor.yy43 = NULL; + buffer_free(yymsp[-3].minor.yy43); + yymsp[-3].minor.yy43 = NULL; + yymsp[0].minor.yy41->free(yymsp[0].minor.yy41); + yymsp[0].minor.yy41 = NULL; } -#line 1224 "configparser.c" - yy_destructor(15,&yymsp[-6].minor); - yy_destructor(17,&yymsp[-4].minor); - yy_destructor(18,&yymsp[-2].minor); +#line 1315 "configparser.c" + yy_destructor(16,&yymsp[-6].minor); + yy_destructor(18,&yymsp[-4].minor); + yy_destructor(19,&yymsp[-2].minor); break; - case 29: -#line 477 "./configparser.y" -{ - yygotominor.yy29 = CONFIG_COND_EQ; -} -#line 1234 "configparser.c" - yy_destructor(19,&yymsp[0].minor); - break; - case 30: -#line 480 "./configparser.y" + case 33: +#line 529 "../../src/configparser.y" { - yygotominor.yy29 = CONFIG_COND_MATCH; + yygotominor.yy27 = CONFIG_COND_EQ; } -#line 1242 "configparser.c" +#line 1325 "configparser.c" yy_destructor(20,&yymsp[0].minor); break; - case 31: -#line 483 "./configparser.y" + case 34: +#line 532 "../../src/configparser.y" { - yygotominor.yy29 = CONFIG_COND_NE; + yygotominor.yy27 = CONFIG_COND_MATCH; } -#line 1250 "configparser.c" +#line 1333 "configparser.c" yy_destructor(21,&yymsp[0].minor); break; - case 32: -#line 486 "./configparser.y" + case 35: +#line 535 "../../src/configparser.y" { - yygotominor.yy29 = CONFIG_COND_NOMATCH; + yygotominor.yy27 = CONFIG_COND_NE; } -#line 1258 "configparser.c" +#line 1341 "configparser.c" yy_destructor(22,&yymsp[0].minor); break; - case 33: -#line 490 "./configparser.y" + case 36: +#line 538 "../../src/configparser.y" +{ + yygotominor.yy27 = CONFIG_COND_NOMATCH; +} +#line 1349 "configparser.c" + yy_destructor(23,&yymsp[0].minor); + break; + case 37: +#line 542 "../../src/configparser.y" { - yygotominor.yy1 = NULL; + yygotominor.yy43 = NULL; if (ctx->ok) { - if (yymsp[0].minor.yy43->type == TYPE_STRING) { - yygotominor.yy1 = buffer_init_buffer(((data_string*)yymsp[0].minor.yy43)->value); - } else if (yymsp[0].minor.yy43->type == TYPE_INTEGER) { - yygotominor.yy1 = buffer_init(); - buffer_copy_long(yygotominor.yy1, ((data_integer *)yymsp[0].minor.yy43)->value); + if (yymsp[0].minor.yy41->type == TYPE_STRING) { + yygotominor.yy43 = buffer_init_buffer(((data_string*)yymsp[0].minor.yy41)->value); + } else if (yymsp[0].minor.yy41->type == TYPE_INTEGER) { + yygotominor.yy43 = buffer_init(); + buffer_copy_long(yygotominor.yy43, ((data_integer *)yymsp[0].minor.yy41)->value); } else { fprintf(stderr, "operand must be string"); ctx->ok = 0; } } - yymsp[0].minor.yy43->free(yymsp[0].minor.yy43); - yymsp[0].minor.yy43 = NULL; + yymsp[0].minor.yy41->free(yymsp[0].minor.yy41); + yymsp[0].minor.yy41 = NULL; } -#line 1279 "configparser.c" +#line 1370 "configparser.c" break; - case 34: -#line 507 "./configparser.y" + case 38: +#line 559 "../../src/configparser.y" { if (ctx->ok) { - if (0 != config_parse_file(ctx->srv, ctx, yymsp[0].minor.yy1->ptr)) { + if (0 != config_parse_file(ctx->srv, ctx, yymsp[0].minor.yy43->ptr)) { ctx->ok = 0; } - buffer_free(yymsp[0].minor.yy1); - yymsp[0].minor.yy1 = NULL; + buffer_free(yymsp[0].minor.yy43); + yymsp[0].minor.yy43 = NULL; } } -#line 1292 "configparser.c" - yy_destructor(23,&yymsp[-1].minor); +#line 1383 "configparser.c" + yy_destructor(24,&yymsp[-1].minor); break; - case 35: -#line 517 "./configparser.y" + case 39: +#line 569 "../../src/configparser.y" { if (ctx->ok) { - if (0 != config_parse_cmd(ctx->srv, ctx, yymsp[0].minor.yy1->ptr)) { + if (0 != config_parse_cmd(ctx->srv, ctx, yymsp[0].minor.yy43->ptr)) { ctx->ok = 0; } - buffer_free(yymsp[0].minor.yy1); - yymsp[0].minor.yy1 = NULL; + buffer_free(yymsp[0].minor.yy43); + yymsp[0].minor.yy43 = NULL; } } -#line 1306 "configparser.c" - yy_destructor(24,&yymsp[-1].minor); +#line 1397 "configparser.c" + yy_destructor(25,&yymsp[-1].minor); break; }; yygoto = yyRuleInfo[yyruleno].lhs; @@ -1333,11 +1424,11 @@ static void yy_parse_failed( while( yypParser->yyidx>=0 ) yy_pop_parser_stack(yypParser); /* Here code is inserted which will be executed whenever the ** parser fails */ -#line 125 "./configparser.y" +#line 108 "../../src/configparser.y" ctx->ok = 0; -#line 1340 "configparser.c" +#line 1431 "configparser.c" configparserARG_STORE; /* Suppress warning about unused %extra_argument variable */ } @@ -1350,6 +1441,8 @@ static void yy_syntax_error( YYMINORTYPE yyminor /* The minor type of the error token */ ){ configparserARG_FETCH; + UNUSED(yymajor); + UNUSED(yyminor); #define TOKEN (yyminor.yy0) configparserARG_STORE; /* Suppress warning about unused %extra_argument variable */ } @@ -1444,7 +1537,7 @@ void configparser( #ifdef YYERRORSYMBOL /* A syntax error has occurred. ** The response to an error depends upon whether or not the - ** grammar defines an error token "ERROR". + ** grammar defines an error token "ERROR". ** ** This is what we do if the grammar does define ERROR: ** diff --git a/src/configparser.h b/src/configparser.h index 9490e58..1610621 100644 --- a/src/configparser.h +++ b/src/configparser.h @@ -9,16 +9,17 @@ #define TK_RPARAN 9 #define TK_COMMA 10 #define TK_ARRAY_ASSIGN 11 -#define TK_ELSE 12 +#define TK_GLOBAL 12 #define TK_LCURLY 13 #define TK_RCURLY 14 -#define TK_DOLLAR 15 -#define TK_SRVVARNAME 16 -#define TK_LBRACKET 17 -#define TK_RBRACKET 18 -#define TK_EQ 19 -#define TK_MATCH 20 -#define TK_NE 21 -#define TK_NOMATCH 22 -#define TK_INCLUDE 23 -#define TK_INCLUDE_SHELL 24 +#define TK_ELSE 15 +#define TK_DOLLAR 16 +#define TK_SRVVARNAME 17 +#define TK_LBRACKET 18 +#define TK_RBRACKET 19 +#define TK_EQ 20 +#define TK_MATCH 21 +#define TK_NE 22 +#define TK_NOMATCH 23 +#define TK_INCLUDE 24 +#define TK_INCLUDE_SHELL 25 diff --git a/src/configparser.y b/src/configparser.y index 767024f..aa6fe98 100644 --- a/src/configparser.y +++ b/src/configparser.y @@ -3,16 +3,14 @@ %name configparser %include { -#include <assert.h> -#include <stdio.h> -#include <string.h> -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif #include "configfile.h" #include "buffer.h" #include "array.h" +#include <assert.h> +#include <stdio.h> +#include <string.h> + static void configparser_push(config_t *ctx, data_config *dc, int isnew) { if (isnew) { dc->context_ndx = ctx->all_configs->used; @@ -21,6 +19,10 @@ static void configparser_push(config_t *ctx, data_config *dc, int isnew) { dc->parent = ctx->current; array_insert_unique(dc->parent->childs, (data_unset *)dc); } + if (ctx->configs_stack->used > 0 && ctx->current->context_ndx == 0) { + fprintf(stderr, "Cannot use conditionals inside a global { ... } block\n"); + exit(-1); + } array_insert_unique(ctx->configs_stack, (data_unset *)ctx->current); ctx->current = dc; } @@ -33,43 +35,25 @@ static data_config *configparser_pop(config_t *ctx) { /* return a copied variable */ static data_unset *configparser_get_variable(config_t *ctx, const buffer *key) { - if (strncmp(key->ptr, "env.", sizeof("env.") - 1) == 0) { - char *env; - - if (NULL != (env = getenv(key->ptr + 4))) { - data_string *ds; - ds = data_string_init(); - buffer_append_string(ds->value, env); - return (data_unset *)ds; - } - - fprintf(stderr, "Undefined env variable: %s\n", key->ptr + 4); - ctx->ok = 0; - - return NULL; - } else { - data_unset *du; - data_config *dc; + data_unset *du; + data_config *dc; #if 0 - fprintf(stderr, "get var %s\n", key->ptr); + fprintf(stderr, "get var %s\n", key->ptr); #endif - for (dc = ctx->current; dc; dc = dc->parent) { + for (dc = ctx->current; dc; dc = dc->parent) { #if 0 - fprintf(stderr, "get var on block: %s\n", dc->key->ptr); - array_print(dc->value, 0); + fprintf(stderr, "get var on block: %s\n", dc->key->ptr); + array_print(dc->value, 0); #endif - if (NULL != (du = array_get_element(dc->value, key->ptr))) { - return du->copy(du); - } + if (NULL != (du = array_get_element(dc->value, key->ptr))) { + return du->copy(du); } - fprintf(stderr, "Undefined config variable: %s\n", key->ptr); - ctx->ok = 0; - return NULL; } + return NULL; } -/* op1 is to be eat/return by this function, op1->key is not cared +/* op1 is to be eat/return by this function if success, op1->key is not cared op2 is left untouch, unreferenced */ data_unset *configparser_merge_data(data_unset *op1, const data_unset *op2) { @@ -86,8 +70,7 @@ data_unset *configparser_merge_data(data_unset *op1, const data_unset *op2) { op1->free(op1); return (data_unset *)ds; } else { - fprintf(stderr, "data type mismatch, cannot be merge\n"); - op1->free(op1); + fprintf(stderr, "data type mismatch, cannot merge\n"); return NULL; } } @@ -130,6 +113,7 @@ input ::= metalines. metalines ::= metalines metaline. metalines ::= . metaline ::= varline. +metaline ::= global. metaline ::= condlines(A) EOL. { A = NULL; } metaline ::= include. metaline ::= include_shell. @@ -140,6 +124,7 @@ metaline ::= EOL. %type aelement {data_unset *} %type condline {data_config *} %type condlines {data_config *} +%type global {data_config *} %type aelements {array *} %type array {array *} %type key {buffer *} @@ -159,17 +144,24 @@ metaline ::= EOL. %token_destructor { buffer_free($$); } varline ::= key(A) ASSIGN expression(B). { - buffer_copy_string_buffer(B->key, A); - if (NULL == array_get_element(ctx->current->value, B->key->ptr)) { - array_insert_unique(ctx->current->value, B); - B = NULL; - } else { - fprintf(stderr, "Duplicate config variable in conditional %d %s: %s\n", - ctx->current->context_ndx, - ctx->current->key->ptr, B->key->ptr); - ctx->ok = 0; - B->free(B); - B = NULL; + if (ctx->ok) { + buffer_copy_string_buffer(B->key, A); + if (strncmp(A->ptr, "env.", sizeof("env.") - 1) == 0) { + fprintf(stderr, "Setting env variable is not supported in conditional %d %s: %s\n", + ctx->current->context_ndx, + ctx->current->key->ptr, A->ptr); + ctx->ok = 0; + } else if (NULL == array_get_element(ctx->current->value, B->key->ptr)) { + array_insert_unique(ctx->current->value, B); + B = NULL; + } else { + fprintf(stderr, "Duplicate config variable in conditional %d %s: %s\n", + ctx->current->context_ndx, + ctx->current->key->ptr, B->key->ptr); + ctx->ok = 0; + B->free(B); + B = NULL; + } } buffer_free(A); A = NULL; @@ -179,7 +171,12 @@ varline ::= key(A) APPEND expression(B). { array *vars = ctx->current->value; data_unset *du; - if (NULL != (du = array_get_element(vars, A->ptr))) { + if (strncmp(A->ptr, "env.", sizeof("env.") - 1) == 0) { + fprintf(stderr, "Appending env variable is not supported in conditional %d %s: %s\n", + ctx->current->context_ndx, + ctx->current->key->ptr, A->ptr); + ctx->ok = 0; + } else if (NULL != (du = array_get_element(vars, A->ptr))) { /* exists in current block */ du = configparser_merge_data(du, B); if (NULL == du) { @@ -189,6 +186,7 @@ varline ::= key(A) APPEND expression(B). { buffer_copy_string_buffer(du->key, A); array_replace(vars, du); } + B->free(B); } else if (NULL != (du = configparser_get_variable(ctx, A))) { du = configparser_merge_data(du, B); if (NULL == du) { @@ -198,15 +196,13 @@ varline ::= key(A) APPEND expression(B). { buffer_copy_string_buffer(du->key, A); array_insert_unique(ctx->current->value, du); } + B->free(B); } else { - fprintf(stderr, "Undefined config variable in conditional %d %s: %s\n", - ctx->current->context_ndx, - ctx->current->key->ptr, A->ptr); - ctx->ok = 0; + buffer_copy_string_buffer(B->key, A); + array_insert_unique(ctx->current->value, B); } buffer_free(A); A = NULL; - B->free(B); B = NULL; } @@ -238,7 +234,24 @@ expression(A) ::= value(B). { } value(A) ::= key(B). { - A = configparser_get_variable(ctx, B); + A = NULL; + if (strncmp(B->ptr, "env.", sizeof("env.") - 1) == 0) { + char *env; + + if (NULL != (env = getenv(B->ptr + 4))) { + data_string *ds; + ds = data_string_init(); + buffer_append_string(ds->value, env); + A = (data_unset *)ds; + } + else { + fprintf(stderr, "Undefined env variable: %s\n", B->ptr + 4); + ctx->ok = 0; + } + } else if (NULL == (A = configparser_get_variable(ctx, B))) { + fprintf(stderr, "Undefined config variable: %s\n", B->ptr); + ctx->ok = 0; + } if (!A) { /* make a dummy so it won't crash */ A = (data_unset *)data_string_init(); @@ -266,6 +279,9 @@ value(A) ::= array(B). { ((data_array *)(A))->value = B; B = NULL; } +array(A) ::= LPARAN RPARAN. { + A = array_init(); +} array(A) ::= LPARAN aelements(B) RPARAN. { A = B; B = NULL; @@ -277,13 +293,13 @@ aelements(A) ::= aelements(C) COMMA aelement(B). { array_insert_unique(C, B); B = NULL; } else { - fprintf(stderr, "Duplicate array-key: %s\n", + fprintf(stderr, "Duplicate array-key: %s\n", B->key->ptr); ctx->ok = 0; B->free(B); B = NULL; } - + A = C; C = NULL; } @@ -307,7 +323,7 @@ aelement(A) ::= stringop(B) ARRAY_ASSIGN expression(C). { buffer_copy_string_buffer(C->key, B); buffer_free(B); B = NULL; - + A = C; C = NULL; } @@ -315,8 +331,29 @@ aelement(A) ::= stringop(B) ARRAY_ASSIGN expression(C). { eols ::= EOL. eols ::= . +globalstart ::= GLOBAL. { + data_config *dc; + dc = (data_config *)array_get_element(ctx->srv->config_context, "global"); + assert(dc); + configparser_push(ctx, dc, 0); +} + +global(A) ::= globalstart LCURLY metalines RCURLY. { + data_config *cur; + + cur = ctx->current; + configparser_pop(ctx); + + assert(cur && ctx->current); + + A = cur; +} + condlines(A) ::= condlines(B) eols ELSE condline(C). { - assert(B->context_ndx < C->context_ndx); + if (B->context_ndx >= C->context_ndx) { + fprintf(stderr, "unreachable else condition\n"); + ctx->ok = 0; + } C->prev = B; B->next = C; A = C; @@ -331,7 +368,7 @@ condlines(A) ::= condline(B). { condline(A) ::= context LCURLY metalines RCURLY. { data_config *cur; - + cur = ctx->current; configparser_pop(ctx); @@ -375,7 +412,7 @@ context ::= DOLLAR SRVVARNAME(B) LBRACKET stringop(C) RBRACKET cond(E) expressio buffer_append_string_buffer(b, op); rvalue = ((data_string*)D)->value; buffer_append_string_buffer(b, rvalue); - + if (NULL != (dc = (data_config *)array_get_element(ctx->all_configs, b->ptr))) { configparser_push(ctx, dc, 0); } else { @@ -388,15 +425,22 @@ context ::= DOLLAR SRVVARNAME(B) LBRACKET stringop(C) RBRACKET cond(E) expressio { COMP_HTTP_URL, CONST_STR_LEN("HTTP[\"url\"]" ) }, { COMP_HTTP_HOST, CONST_STR_LEN("HTTP[\"host\"]" ) }, { COMP_HTTP_REFERER, CONST_STR_LEN("HTTP[\"referer\"]" ) }, - { COMP_HTTP_USERAGENT, CONST_STR_LEN("HTTP[\"useragent\"]" ) }, + { COMP_HTTP_USER_AGENT, CONST_STR_LEN("HTTP[\"useragent\"]" ) }, + { COMP_HTTP_USER_AGENT, CONST_STR_LEN("HTTP[\"user-agent\"]" ) }, + { COMP_HTTP_LANGUAGE, CONST_STR_LEN("HTTP[\"language\"]" ) }, { COMP_HTTP_COOKIE, CONST_STR_LEN("HTTP[\"cookie\"]" ) }, - { COMP_HTTP_REMOTEIP, CONST_STR_LEN("HTTP[\"remoteip\"]" ) }, + { COMP_HTTP_REMOTE_IP, CONST_STR_LEN("HTTP[\"remoteip\"]" ) }, + { COMP_HTTP_REMOTE_IP, CONST_STR_LEN("HTTP[\"remote-ip\"]" ) }, + { COMP_HTTP_QUERY_STRING, CONST_STR_LEN("HTTP[\"querystring\"]") }, + { COMP_HTTP_QUERY_STRING, CONST_STR_LEN("HTTP[\"query-string\"]") }, + { COMP_HTTP_REQUEST_METHOD, CONST_STR_LEN("HTTP[\"request-method\"]") }, + { COMP_HTTP_SCHEME, CONST_STR_LEN("HTTP[\"scheme\"]" ) }, { COMP_UNSET, NULL, 0 }, }; size_t i; dc = data_config_init(); - + buffer_copy_string_buffer(dc->key, b); buffer_copy_string_buffer(dc->op, op); buffer_copy_string_buffer(dc->comp_key, B); @@ -404,7 +448,7 @@ context ::= DOLLAR SRVVARNAME(B) LBRACKET stringop(C) RBRACKET cond(E) expressio buffer_append_string_buffer(dc->comp_key, C); buffer_append_string_len(dc->comp_key, CONST_STR_LEN("\"]")); dc->cond = E; - + for (i = 0; comps[i].comp_key; i ++) { if (buffer_is_equal_string( dc->comp_key, comps[i].comp_key, comps[i].len)) { @@ -426,29 +470,37 @@ context ::= DOLLAR SRVVARNAME(B) LBRACKET stringop(C) RBRACKET cond(E) expressio case CONFIG_COND_MATCH: { #ifdef HAVE_PCRE_H const char *errptr; - int erroff; - - if (NULL == (dc->regex = + int erroff, captures; + + if (NULL == (dc->regex = pcre_compile(rvalue->ptr, 0, &errptr, &erroff, NULL))) { dc->string = buffer_init_string(errptr); dc->cond = CONFIG_COND_UNSET; - fprintf(stderr, "parsing regex failed: %s -> %s at offset %d\n", + fprintf(stderr, "parsing regex failed: %s -> %s at offset %d\n", rvalue->ptr, errptr, erroff); ctx->ok = 0; } else if (NULL == (dc->regex_study = - pcre_study(dc->regex, 0, &errptr)) && + pcre_study(dc->regex, 0, &errptr)) && errptr != NULL) { - fprintf(stderr, "studying regex failed: %s -> %s\n", + fprintf(stderr, "studying regex failed: %s -> %s\n", rvalue->ptr, errptr); ctx->ok = 0; + } else if (0 != (pcre_fullinfo(dc->regex, dc->regex_study, PCRE_INFO_CAPTURECOUNT, &captures))) { + fprintf(stderr, "getting capture count for regex failed: %s\n", + rvalue->ptr); + ctx->ok = 0; + } else if (captures > 9) { + fprintf(stderr, "Too many captures in regex, use (?:...) instead of (...): %s\n", + rvalue->ptr); + ctx->ok = 0; } else { dc->string = buffer_init_buffer(rvalue); } #else fprintf(stderr, "can't handle '$%s[%s] =~ ...' as you compiled without pcre support. \n" - "(perhaps just a missing pcre-devel package ?) \n", + "(perhaps just a missing pcre-devel package ?) \n", B->ptr, C->ptr); ctx->ok = 0; #endif @@ -456,12 +508,12 @@ context ::= DOLLAR SRVVARNAME(B) LBRACKET stringop(C) RBRACKET cond(E) expressio } default: - fprintf(stderr, "unknown condition for $%s[%s]\n", + fprintf(stderr, "unknown condition for $%s[%s]\n", B->ptr, C->ptr); ctx->ok = 0; break; } - + configparser_push(ctx, dc, 1); } diff --git a/src/connections-glue.c b/src/connections-glue.c index ac6d267..5ef7a1e 100644 --- a/src/connections-glue.c +++ b/src/connections-glue.c @@ -1,4 +1,5 @@ #include "base.h" +#include "connections.h" const char *connection_get_state(connection_state_t state) { switch (state) { @@ -13,7 +14,7 @@ const char *connection_get_state(connection_state_t state) { case CON_STATE_REQUEST_END: return "req-end"; case CON_STATE_RESPONSE_START: return "resp-start"; case CON_STATE_RESPONSE_END: return "resp-end"; - default: return "(unknown)"; + default: return "(unknown)"; } } @@ -30,15 +31,15 @@ const char *connection_get_short_state(connection_state_t state) { case CON_STATE_REQUEST_END: return "Q"; case CON_STATE_RESPONSE_START: return "s"; case CON_STATE_RESPONSE_END: return "S"; - default: return "x"; + default: return "x"; } } int connection_set_state(server *srv, connection *con, connection_state_t state) { UNUSED(srv); - + con->state = state; - + return 0; } diff --git a/src/connections.c b/src/connections.c index ea3a66c..48ca60f 100644 --- a/src/connections.c +++ b/src/connections.c @@ -1,13 +1,3 @@ -#include <sys/stat.h> - -#include <stdlib.h> -#include <stdio.h> -#include <unistd.h> -#include <errno.h> -#include <string.h> -#include <fcntl.h> -#include <assert.h> - #include "buffer.h" #include "server.h" #include "log.h" @@ -25,9 +15,19 @@ #include "inet_ntop_cache.h" +#include <sys/stat.h> + +#include <stdlib.h> +#include <stdio.h> +#include <unistd.h> +#include <errno.h> +#include <string.h> +#include <fcntl.h> +#include <assert.h> + #ifdef USE_OPENSSL -# include <openssl/ssl.h> -# include <openssl/err.h> +# include <openssl/ssl.h> +# include <openssl/err.h> #endif #ifdef HAVE_SYS_FILIO_H @@ -43,7 +43,7 @@ typedef struct { static connection *connections_get_new_connection(server *srv) { connections *conns = srv->conns; size_t i; - + if (conns->size == 0) { conns->size = 128; conns->ptr = NULL; @@ -54,21 +54,21 @@ static connection *connections_get_new_connection(server *srv) { } else if (conns->size == conns->used) { conns->size += 128; conns->ptr = realloc(conns->ptr, sizeof(*conns->ptr) * conns->size); - + for (i = conns->used; i < conns->size; i++) { conns->ptr[i] = connection_init(srv); } } connection_reset(srv, conns->ptr[conns->used]); -#if 0 +#if 0 fprintf(stderr, "%s.%d: add: ", __FILE__, __LINE__); for (i = 0; i < conns->used + 1; i++) { fprintf(stderr, "%d ", conns->ptr[i]->fd); } fprintf(stderr, "\n"); -#endif - +#endif + conns->ptr[conns->used]->ndx = conns->used; return conns->ptr[conns->used++]; } @@ -77,26 +77,31 @@ static int connection_del(server *srv, connection *con) { size_t i; connections *conns = srv->conns; connection *temp; - + if (con == NULL) return -1; - + if (-1 == con->ndx) return -1; - + + buffer_reset(con->uri.authority); + buffer_reset(con->uri.path); + buffer_reset(con->uri.query); + buffer_reset(con->request.orig_uri); + i = con->ndx; - + /* not last element */ - + if (i != conns->used - 1) { temp = conns->ptr[i]; conns->ptr[i] = conns->ptr[conns->used - 1]; conns->ptr[conns->used - 1] = temp; - + conns->ptr[i]->ndx = i; conns->ptr[conns->used - 1]->ndx = -1; } - + conns->used--; - + con->ndx = -1; #if 0 fprintf(stderr, "%s.%d: del: (%d)", __FILE__, __LINE__, conns->used); @@ -104,7 +109,7 @@ static int connection_del(server *srv, connection *con) { fprintf(stderr, "%d ", conns->ptr[i]->fd); } fprintf(stderr, "\n"); -#endif +#endif return 0; } @@ -112,14 +117,14 @@ int connection_close(server *srv, connection *con) { #ifdef USE_OPENSSL server_socket *srv_sock = con->srv_socket; #endif - + #ifdef USE_OPENSSL if (srv_sock->is_ssl) { if (con->ssl) SSL_free(con->ssl); con->ssl = NULL; } #endif - + fdevent_event_del(srv->ev, &(con->fde_ndx), con->fd); fdevent_unregister(srv->ev, con->fd); #ifdef __WIN32 @@ -133,184 +138,254 @@ int connection_close(server *srv, connection *con) { "(warning) close:", con->fd, strerror(errno)); } #endif - + srv->cur_fds--; #if 0 log_error_write(srv, __FILE__, __LINE__, "sd", "closed()", con->fd); #endif - + connection_del(srv, con); connection_set_state(srv, con, CON_STATE_CONNECT); - + return 0; } #if 0 static void dump_packet(const unsigned char *data, size_t len) { size_t i, j; - + if (len == 0) return; - + for (i = 0; i < len; i++) { if (i % 16 == 0) fprintf(stderr, " "); - + fprintf(stderr, "%02x ", data[i]); - + if ((i + 1) % 16 == 0) { fprintf(stderr, " "); for (j = 0; j <= i % 16; j++) { unsigned char c; - + if (i-15+j >= len) break; - + c = data[i-15+j]; - + fprintf(stderr, "%c", c > 32 && c < 128 ? c : '.'); } - + fprintf(stderr, "\n"); } } - + if (len % 16 != 0) { for (j = i % 16; j < 16; j++) { fprintf(stderr, " "); } - + fprintf(stderr, " "); for (j = i & ~0xf; j < len; j++) { unsigned char c; - + c = data[j]; fprintf(stderr, "%c", c > 32 && c < 128 ? c : '.'); } fprintf(stderr, "\n"); } } -#endif +#endif -static int connection_handle_read(server *srv, connection *con) { - int len; - buffer *b; - int toread; +static int connection_handle_read_ssl(server *srv, connection *con) { #ifdef USE_OPENSSL - server_socket *srv_sock = con->srv_socket; -#endif + int r, ssl_err, len, count = 0, read_offset, toread; + buffer *b = NULL; - b = chunkqueue_get_append_buffer(con->read_queue); - buffer_prepare_copy(b, 4096); + if (!con->conf.is_ssl) return -1; -#ifdef USE_OPENSSL - if (srv_sock->is_ssl) { - len = SSL_read(con->ssl, b->ptr, b->size - 1); - } else { - if (ioctl(con->fd, FIONREAD, &toread)) { - log_error_write(srv, __FILE__, __LINE__, "sd", - "unexpected end-of-file:", - con->fd); + ERR_clear_error(); + do { + if (NULL != con->read_queue->last) { + b = con->read_queue->last->mem; + } + + if (NULL == b || b->size - b->used < 1024) { + b = chunkqueue_get_append_buffer(con->read_queue); + len = SSL_pending(con->ssl); + if (len < 4*1024) len = 4*1024; /* always alloc >= 4k buffer */ + buffer_prepare_copy(b, len + 1); + + /* overwrite everything with 0 */ + memset(b->ptr, 0, b->size); + } + + read_offset = (b->used > 0) ? b->used - 1 : 0; + toread = b->size - 1 - read_offset; + + len = SSL_read(con->ssl, b->ptr + read_offset, toread); + + if (con->renegotiations > 1 && con->conf.ssl_disable_client_renegotiation) { + connection_set_state(srv, con, CON_STATE_ERROR); + log_error_write(srv, __FILE__, __LINE__, "s", "SSL: renegotiation initiated by client"); return -1; } - buffer_prepare_copy(b, toread); - len = read(con->fd, b->ptr, b->size - 1); - } -#elif defined(__WIN32) - len = recv(con->fd, b->ptr, b->size - 1, 0); -#else - if (ioctl(con->fd, FIONREAD, &toread)) { - log_error_write(srv, __FILE__, __LINE__, "sd", - "unexpected end-of-file:", - con->fd); - return -1; - } - buffer_prepare_copy(b, toread); + if (len > 0) { + if (b->used > 0) b->used--; + b->used += len; + b->ptr[b->used++] = '\0'; + + con->bytes_read += len; + + count += len; + } + } while (len == toread && count < MAX_READ_LIMIT); + - len = read(con->fd, b->ptr, b->size - 1); -#endif - if (len < 0) { - con->is_readable = 0; - -#ifdef USE_OPENSSL - if (srv_sock->is_ssl) { - int r, ssl_err; - - switch ((r = SSL_get_error(con->ssl, len))) { - case SSL_ERROR_WANT_READ: - return 0; - case SSL_ERROR_SYSCALL: - /** - * man SSL_get_error() - * - * SSL_ERROR_SYSCALL - * Some I/O error occurred. The OpenSSL error queue may contain more - * information on the error. If the error queue is empty (i.e. - * ERR_get_error() returns 0), ret can be used to find out more about - * the error: If ret == 0, an EOF was observed that violates the - * protocol. If ret == -1, the underlying BIO reported an I/O error - * (for socket I/O on Unix systems, consult errno for details). - * - */ - while((ssl_err = ERR_get_error())) { - /* get all errors from the error-queue */ - log_error_write(srv, __FILE__, __LINE__, "sds", "SSL:", - r, ERR_error_string(ssl_err, NULL)); - } + int oerrno = errno; + switch ((r = SSL_get_error(con->ssl, len))) { + case SSL_ERROR_WANT_READ: + case SSL_ERROR_WANT_WRITE: + con->is_readable = 0; + + /* the manual says we have to call SSL_read with the same arguments next time. + * we ignore this restriction; no one has complained about it in 1.5 yet, so it probably works anyway. + */ - switch(errno) { - default: - log_error_write(srv, __FILE__, __LINE__, "sddds", "SSL:", - len, r, errno, - strerror(errno)); - break; - } - - break; - case SSL_ERROR_ZERO_RETURN: - /* clean shutdown on the remote side */ - - if (r == 0) { - /* FIXME: later */ - } - - /* fall thourgh */ + return 0; + case SSL_ERROR_SYSCALL: + /** + * man SSL_get_error() + * + * SSL_ERROR_SYSCALL + * Some I/O error occurred. The OpenSSL error queue may contain more + * information on the error. If the error queue is empty (i.e. + * ERR_get_error() returns 0), ret can be used to find out more about + * the error: If ret == 0, an EOF was observed that violates the + * protocol. If ret == -1, the underlying BIO reported an I/O error + * (for socket I/O on Unix systems, consult errno for details). + * + */ + while((ssl_err = ERR_get_error())) { + /* get all errors from the error-queue */ + log_error_write(srv, __FILE__, __LINE__, "sds", "SSL:", + r, ERR_error_string(ssl_err, NULL)); + } + + switch(oerrno) { default: - while((ssl_err = ERR_get_error())) { - /* get all errors from the error-queue */ - log_error_write(srv, __FILE__, __LINE__, "sds", "SSL:", - r, ERR_error_string(ssl_err, NULL)); - } + log_error_write(srv, __FILE__, __LINE__, "sddds", "SSL:", + len, r, oerrno, + strerror(oerrno)); break; } - } else { - if (errno == EAGAIN) return 0; - if (errno == EINTR) { - /* we have been interrupted before we could read */ - con->is_readable = 1; - return 0; + + break; + case SSL_ERROR_ZERO_RETURN: + /* clean shutdown on the remote side */ + + if (r == 0) { + /* FIXME: later */ } - - if (errno != ECONNRESET) { - /* expected for keep-alive */ - log_error_write(srv, __FILE__, __LINE__, "ssd", "connection closed - read failed: ", strerror(errno), errno); + + /* fall thourgh */ + default: + while((ssl_err = ERR_get_error())) { + switch (ERR_GET_REASON(ssl_err)) { + case SSL_R_SSL_HANDSHAKE_FAILURE: + case SSL_R_TLSV1_ALERT_UNKNOWN_CA: + case SSL_R_SSLV3_ALERT_CERTIFICATE_UNKNOWN: + case SSL_R_SSLV3_ALERT_BAD_CERTIFICATE: + if (!con->conf.log_ssl_noise) continue; + break; + default: + break; + } + /* get all errors from the error-queue */ + log_error_write(srv, __FILE__, __LINE__, "sds", "SSL:", + r, ERR_error_string(ssl_err, NULL)); } + break; } + + connection_set_state(srv, con, CON_STATE_ERROR); + + return -1; + } else if (len == 0) { + con->is_readable = 0; + /* the other end close the connection -> KEEP-ALIVE */ + + return -2; + } else { + joblist_append(srv, con); + } + + return 0; #else + UNUSED(srv); + UNUSED(con); + return -1; +#endif +} + +/* 0: everything ok, -1: error, -2: con closed */ +static int connection_handle_read(server *srv, connection *con) { + int len; + buffer *b; + int toread, read_offset; + + if (con->conf.is_ssl) { + return connection_handle_read_ssl(srv, con); + } + + b = (NULL != con->read_queue->last) ? con->read_queue->last->mem : NULL; + + /* default size for chunks is 4kb; only use bigger chunks if FIONREAD tells + * us more than 4kb is available + * if FIONREAD doesn't signal a big chunk we fill the previous buffer + * if it has >= 1kb free + */ +#if defined(__WIN32) + if (NULL == b || b->size - b->used < 1024) { + b = chunkqueue_get_append_buffer(con->read_queue); + buffer_prepare_copy(b, 4 * 1024); + } + + read_offset = (b->used == 0) ? 0 : b->used - 1; + len = recv(con->fd, b->ptr + read_offset, b->size - 1 - read_offset, 0); +#else + if (ioctl(con->fd, FIONREAD, &toread) || toread == 0 || toread <= 4*1024) { + if (NULL == b || b->size - b->used < 1024) { + b = chunkqueue_get_append_buffer(con->read_queue); + buffer_prepare_copy(b, 4 * 1024); + } + } else { + if (toread > MAX_READ_LIMIT) toread = MAX_READ_LIMIT; + b = chunkqueue_get_append_buffer(con->read_queue); + buffer_prepare_copy(b, toread + 1); + } + + read_offset = (b->used == 0) ? 0 : b->used - 1; + len = read(con->fd, b->ptr + read_offset, b->size - 1 - read_offset); +#endif + + if (len < 0) { + con->is_readable = 0; + if (errno == EAGAIN) return 0; if (errno == EINTR) { /* we have been interrupted before we could read */ con->is_readable = 1; return 0; } - + if (errno != ECONNRESET) { /* expected for keep-alive */ log_error_write(srv, __FILE__, __LINE__, "ssd", "connection closed - read failed: ", strerror(errno), errno); } -#endif + connection_set_state(srv, con, CON_STATE_ERROR); - + return -1; } else if (len == 0) { con->is_readable = 0; @@ -321,18 +396,19 @@ static int connection_handle_read(server *srv, connection *con) { return -2; } else if ((size_t)len < b->size - 1) { /* we got less then expected, wait for the next fd-event */ - + con->is_readable = 0; } - - b->used = len; + + if (b->used > 0) b->used--; + b->used += len; b->ptr[b->used++] = '\0'; - + con->bytes_read += len; #if 0 dump_packet(b->ptr, len); #endif - + return 0; } @@ -344,23 +420,29 @@ static int connection_handle_write_prepare(server *srv, connection *con) { case HTTP_METHOD_POST: case HTTP_METHOD_HEAD: case HTTP_METHOD_PUT: + case HTTP_METHOD_PATCH: case HTTP_METHOD_MKCOL: case HTTP_METHOD_DELETE: case HTTP_METHOD_COPY: case HTTP_METHOD_MOVE: case HTTP_METHOD_PROPFIND: case HTTP_METHOD_PROPPATCH: + case HTTP_METHOD_LOCK: + case HTTP_METHOD_UNLOCK: break; case HTTP_METHOD_OPTIONS: /* * 400 is coming from the request-parser BEFORE uri.path is set - * 403 is from the response handler when noone else catched it - * + * 403 is from the response handler when noone else catched it + * * */ - if (con->uri.path->used && + if ((!con->http_status || con->http_status == 200) && con->uri.path->used && con->uri.path->ptr[0] != '*') { response_header_insert(srv, con, CONST_STR_LEN("Allow"), CONST_STR_LEN("OPTIONS, GET, HEAD, POST")); + con->response.transfer_encoding &= ~HTTP_TRANSFER_ENCODING_CHUNKED; + con->parsed_response &= ~HTTP_CONTENT_LENGTH; + con->http_status = 200; con->file_finished = 1; @@ -370,6 +452,7 @@ static int connection_handle_write_prepare(server *srv, connection *con) { default: switch(con->http_status) { case 400: /* bad request */ + case 401: /* authorization required */ case 414: /* overload request header */ case 505: /* unknown protocol */ case 207: /* this was webdav */ @@ -381,152 +464,170 @@ static int connection_handle_write_prepare(server *srv, connection *con) { break; } } - + if (con->http_status == 0) { con->http_status = 403; } - + switch(con->http_status) { - case 400: /* class: header + custom body */ - case 401: - case 403: - case 404: - case 408: - case 411: - case 416: - case 500: - case 501: - case 503: - case 505: + case 204: /* class: header only */ + case 205: + case 304: + /* disable chunked encoding again as we have no body */ + con->response.transfer_encoding &= ~HTTP_TRANSFER_ENCODING_CHUNKED; + con->parsed_response &= ~HTTP_CONTENT_LENGTH; + chunkqueue_reset(con->write_queue); + + con->file_finished = 1; + break; + default: /* class: header + body */ if (con->mode != DIRECT) break; - + + /* only custom body for 4xx and 5xx */ + if (con->http_status < 400 || con->http_status >= 600) break; + con->file_finished = 0; - + buffer_reset(con->physical.path); - + /* try to send static errorfile */ if (!buffer_is_empty(con->conf.errorfile_prefix)) { stat_cache_entry *sce = NULL; - + buffer_copy_string_buffer(con->physical.path, con->conf.errorfile_prefix); - buffer_append_string(con->physical.path, get_http_status_body_name(con->http_status)); - + buffer_append_long(con->physical.path, con->http_status); + buffer_append_string_len(con->physical.path, CONST_STR_LEN(".html")); + if (HANDLER_ERROR != stat_cache_get_entry(srv, con, con->physical.path, &sce)) { con->file_finished = 1; - + http_chunk_append_file(srv, con, con->physical.path, 0, sce->st.st_size); response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_BUF_LEN(sce->content_type)); } } - - if (!con->file_finished) { + + if (!con->file_finished) { buffer *b; - + buffer_reset(con->physical.path); - + con->file_finished = 1; b = chunkqueue_get_append_buffer(con->write_queue); - + /* build default error-page */ - buffer_copy_string(b, + buffer_copy_string_len(b, CONST_STR_LEN( "<?xml version=\"1.0\" encoding=\"iso-8859-1\"?>\n" "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\"\n" " \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n" "<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" lang=\"en\">\n" " <head>\n" - " <title>"); + " <title>")); buffer_append_long(b, con->http_status); - buffer_append_string(b, " - "); + buffer_append_string_len(b, CONST_STR_LEN(" - ")); buffer_append_string(b, get_http_status_name(con->http_status)); - - buffer_append_string(b, + + buffer_append_string_len(b, CONST_STR_LEN( "</title>\n" " </head>\n" " <body>\n" - " <h1>"); + " <h1>")); buffer_append_long(b, con->http_status); - buffer_append_string(b, " - "); + buffer_append_string_len(b, CONST_STR_LEN(" - ")); buffer_append_string(b, get_http_status_name(con->http_status)); - - buffer_append_string(b,"</h1>\n" + + buffer_append_string_len(b, CONST_STR_LEN("</h1>\n" " </body>\n" "</html>\n" - ); - + )); + response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_STR_LEN("text/html")); } - /* fall through */ - case 207: - case 200: /* class: header + body */ - case 302: - break; - - case 206: /* write_queue is already prepared */ - con->file_finished = 1; - - break; - case 205: /* class: header only */ - case 301: - case 304: - default: - /* disable chunked encoding again as we have no body */ - con->response.transfer_encoding &= ~HTTP_TRANSFER_ENCODING_CHUNKED; - chunkqueue_reset(con->write_queue); - - con->file_finished = 1; break; } - if (con->file_finished) { - /* we have all the content and chunked encoding is not used, set a content-length */ - - if ((!(con->parsed_response & HTTP_CONTENT_LENGTH)) && + /* we have all the content and chunked encoding is not used, set a content-length */ + + if ((!(con->parsed_response & HTTP_CONTENT_LENGTH)) && (con->response.transfer_encoding & HTTP_TRANSFER_ENCODING_CHUNKED) == 0) { - buffer_copy_off_t(srv->tmp_buf, chunkqueue_length(con->write_queue)); - - response_header_overwrite(srv, con, CONST_STR_LEN("Content-Length"), CONST_BUF_LEN(srv->tmp_buf)); + off_t qlen = chunkqueue_length(con->write_queue); + + /** + * The Content-Length header only can be sent if we have content: + * - HEAD doesn't have a content-body (but have a content-length) + * - 1xx, 204 and 304 don't have a content-body (RFC 2616 Section 4.3) + * + * Otherwise generate a Content-Length header as chunked encoding is not + * available + */ + if ((con->http_status >= 100 && con->http_status < 200) || + con->http_status == 204 || + con->http_status == 304) { + data_string *ds; + /* no Content-Body, no Content-Length */ + if (NULL != (ds = (data_string*) array_get_element(con->response.headers, "Content-Length"))) { + buffer_reset(ds->value); /* Headers with empty values are ignored for output */ + } + } else if (qlen > 0 || con->request.http_method != HTTP_METHOD_HEAD) { + /* qlen = 0 is important for Redirects (301, ...) as they MAY have + * a content. Browsers are waiting for a Content otherwise + */ + buffer_copy_off_t(srv->tmp_buf, qlen); + + response_header_overwrite(srv, con, CONST_STR_LEN("Content-Length"), CONST_BUF_LEN(srv->tmp_buf)); + } } } else { - /* disable keep-alive if size-info for the body is missing */ - if ((con->parsed_response & HTTP_CONTENT_LENGTH) && + /** + * the file isn't finished yet, but we have all headers + * + * to get keep-alive we either need: + * - Content-Length: ... (HTTP/1.0 and HTTP/1.0) or + * - Transfer-Encoding: chunked (HTTP/1.1) + */ + + if (((con->parsed_response & HTTP_CONTENT_LENGTH) == 0) && ((con->response.transfer_encoding & HTTP_TRANSFER_ENCODING_CHUNKED) == 0)) { con->keep_alive = 0; } - - if (0 == (con->parsed_response & HTTP_CONNECTION)) { - /* (f)cgi did'nt send Connection: header - * - * shall we ? - */ - if (((con->response.transfer_encoding & HTTP_TRANSFER_ENCODING_CHUNKED) == 0) && - (con->parsed_response & HTTP_CONTENT_LENGTH) == 0) { - /* without content_length, no keep-alive */ - - con->keep_alive = 0; - } - } else { + + /** + * if the backend sent a Connection: close, follow the wish + * + * NOTE: if the backend sent Connection: Keep-Alive, but no Content-Length, we + * will close the connection. That's fine. We can always decide the close + * the connection + * + * FIXME: to be nice we should remove the Connection: ... + */ + if (con->parsed_response & HTTP_CONNECTION) { /* a subrequest disable keep-alive although the client wanted it */ if (con->keep_alive && !con->response.keep_alive) { con->keep_alive = 0; - - /* FIXME: we have to drop the Connection: Header from the subrequest */ } } } - + if (con->request.http_method == HTTP_METHOD_HEAD) { + /** + * a HEAD request has the same as a GET + * without the content + */ + con->file_finished = 1; + chunkqueue_reset(con->write_queue); + con->response.transfer_encoding &= ~HTTP_TRANSFER_ENCODING_CHUNKED; } http_response_write_header(srv, con); - + return 0; } static int connection_handle_write(server *srv, connection *con) { - switch(network_write_chunkqueue(srv, con, con->write_queue)) { + switch(network_write_chunkqueue(srv, con, con->write_queue, MAX_WRITE_LIMIT)) { case 0: + con->write_request_ts = srv->cur_ts; if (con->file_finished) { connection_set_state(srv, con, CON_STATE_RESPONSE_END); joblist_append(srv, con); @@ -543,12 +644,13 @@ static int connection_handle_write(server *srv, connection *con) { joblist_append(srv, con); break; case 1: + con->write_request_ts = srv->cur_ts; con->is_writable = 0; - + /* not finished yet -> WRITE */ break; } - + return 0; } @@ -556,11 +658,11 @@ static int connection_handle_write(server *srv, connection *con) { connection *connection_init(server *srv) { connection *con; - + UNUSED(srv); con = calloc(1, sizeof(*con)); - + con->fd = 0; con->ndx = -1; con->fde_ndx = -1; @@ -571,32 +673,35 @@ connection *connection_init(server *srv) { #define CLEAN(x) \ con->x = buffer_init(); - + CLEAN(request.uri); CLEAN(request.request_line); CLEAN(request.request); CLEAN(request.pathinfo); - + CLEAN(request.orig_uri); - + CLEAN(uri.scheme); CLEAN(uri.authority); CLEAN(uri.path); CLEAN(uri.path_raw); CLEAN(uri.query); - + CLEAN(physical.doc_root); CLEAN(physical.path); CLEAN(physical.basedir); CLEAN(physical.rel_path); CLEAN(physical.etag); CLEAN(parse_request); - + CLEAN(authed_user); CLEAN(server_name); CLEAN(error_handler); CLEAN(dst_addr_buf); - +#if defined USE_OPENSSL && ! defined OPENSSL_NO_TLSEXT + CLEAN(tlsext_server_name); +#endif + #undef CLEAN con->write_queue = chunkqueue_init(); con->read_queue = chunkqueue_init(); @@ -606,26 +711,26 @@ connection *connection_init(server *srv) { con->request.headers = array_init(); con->response.headers = array_init(); con->environment = array_init(); - + /* init plugin specific connection structures */ - + con->plugin_ctx = calloc(1, (srv->plugins.used + 1) * sizeof(void *)); - + con->cond_cache = calloc(srv->config_context->used, sizeof(cond_cache_t)); config_setup_connection(srv, con); - + return con; } void connections_free(server *srv) { connections *conns = srv->conns; - size_t i; - + size_t i; + for (i = 0; i < conns->size; i++) { connection *con = conns->ptr[i]; - + connection_reset(srv, con); - + chunkqueue_free(con->write_queue); chunkqueue_free(con->read_queue); chunkqueue_free(con->request_content_queue); @@ -635,125 +740,131 @@ void connections_free(server *srv) { #define CLEAN(x) \ buffer_free(con->x); - + CLEAN(request.uri); CLEAN(request.request_line); CLEAN(request.request); CLEAN(request.pathinfo); - + CLEAN(request.orig_uri); - + CLEAN(uri.scheme); CLEAN(uri.authority); CLEAN(uri.path); CLEAN(uri.path_raw); CLEAN(uri.query); - + CLEAN(physical.doc_root); CLEAN(physical.path); CLEAN(physical.basedir); CLEAN(physical.etag); CLEAN(physical.rel_path); CLEAN(parse_request); - + CLEAN(authed_user); CLEAN(server_name); CLEAN(error_handler); CLEAN(dst_addr_buf); +#if defined USE_OPENSSL && ! defined OPENSSL_NO_TLSEXT + CLEAN(tlsext_server_name); +#endif #undef CLEAN free(con->plugin_ctx); free(con->cond_cache); - + free(con); } - + free(conns->ptr); } int connection_reset(server *srv, connection *con) { size_t i; - + plugins_call_connection_reset(srv, con); - + con->is_readable = 1; con->is_writable = 1; con->http_status = 0; con->file_finished = 0; con->file_started = 0; con->got_response = 0; - + con->parsed_response = 0; - + con->bytes_written = 0; con->bytes_written_cur_second = 0; con->bytes_read = 0; con->bytes_header = 0; con->loops_per_request = 0; - + con->request.http_method = HTTP_METHOD_UNSET; con->request.http_version = HTTP_VERSION_UNSET; - + con->request.http_if_modified_since = NULL; con->request.http_if_none_match = NULL; - + con->response.keep_alive = 0; con->response.content_length = -1; con->response.transfer_encoding = 0; - + con->mode = DIRECT; - + #define CLEAN(x) \ if (con->x) buffer_reset(con->x); - + CLEAN(request.uri); CLEAN(request.request_line); CLEAN(request.pathinfo); CLEAN(request.request); - - CLEAN(request.orig_uri); - + + /* CLEAN(request.orig_uri); */ + CLEAN(uri.scheme); - CLEAN(uri.authority); - CLEAN(uri.path); + /* CLEAN(uri.authority); */ + /* CLEAN(uri.path); */ CLEAN(uri.path_raw); - CLEAN(uri.query); - + /* CLEAN(uri.query); */ + CLEAN(physical.doc_root); CLEAN(physical.path); CLEAN(physical.basedir); CLEAN(physical.rel_path); CLEAN(physical.etag); - + CLEAN(parse_request); - + CLEAN(authed_user); CLEAN(server_name); CLEAN(error_handler); -#undef CLEAN - +#if defined USE_OPENSSL && ! defined OPENSSL_NO_TLSEXT + CLEAN(tlsext_server_name); +#endif +#undef CLEAN + #define CLEAN(x) \ - if (con->x) con->x->used = 0; - + if (con->x) con->x->used = 0; + #undef CLEAN - + #define CLEAN(x) \ con->request.x = NULL; - + CLEAN(http_host); CLEAN(http_range); CLEAN(http_content_type); #undef CLEAN con->request.content_length = 0; - + array_reset(con->request.headers); array_reset(con->response.headers); array_reset(con->environment); - + chunkqueue_reset(con->write_queue); chunkqueue_reset(con->request_content_queue); - /* the plugins should cleanup themself */ + /* the plugins should cleanup themself */ for (i = 0; i < srv->plugins.used; i++) { plugin *p = ((plugin **)(srv->plugins.ptr))[i]; plugin_data *pd = p->data; @@ -766,95 +877,40 @@ int connection_reset(server *srv, connection *con) { con->plugin_ctx[pd->id] = NULL; } - -#if COND_RESULT_UNSET - for (i = srv->config_context->used - 1; i >= 0; i --) { - con->cond_cache[i].result = COND_RESULT_UNSET; - con->cond_cache[i].patterncount = 0; - } -#else - memset(con->cond_cache, 0, sizeof(cond_cache_t) * srv->config_context->used); -#endif - + + /* The cond_cache gets reset in response.c */ + /* config_cond_cache_reset(srv, con); */ + con->header_len = 0; con->in_error_handler = 0; - + config_setup_connection(srv, con); - + return 0; } /** - * - * search for \r\n\r\n - * - * this is a special 32bit version which is using a sliding window for - * the comparisions - * - * how it works: - * - * b: 'abcdefg' - * rnrn: 'cdef' - * - * cmpbuf: abcd != cdef - * cmpbuf: bcde != cdef - * cmpbuf: cdef == cdef -> return &c - * - * cmpbuf and rnrn are treated as 32bit uint and bit-ops are used to - * maintain cmpbuf and rnrn - * - */ - -char *buffer_search_rnrn(buffer *b) { - uint32_t cmpbuf, rnrn; - char *cp; - size_t i; - - if (b->used < 4) return NULL; - - rnrn = ('\r' << 24) | ('\n' << 16) | - ('\r' << 8) | ('\n' << 0); - - cmpbuf = (b->ptr[0] << 24) | (b->ptr[1] << 16) | - (b->ptr[2] << 8) | (b->ptr[3] << 0); - - cp = b->ptr + 4; - for (i = 0; i < b->used - 4; i++) { - if (cmpbuf == rnrn) return cp - 4; - - cmpbuf = (cmpbuf << 8 | *(cp++)) & 0xffffffff; - } - - return NULL; -} -/** * handle all header and content read * * we get called by the state-engine and by the fdevent-handler */ -int connection_handle_read_state(server *srv, connection *con) { - int ostate = con->state; - char *h_term = NULL; - chunk *c; +static int connection_handle_read_state(server *srv, connection *con) { + connection_state_t ostate = con->state; + chunk *c, *last_chunk; + off_t last_offset; chunkqueue *cq = con->read_queue; chunkqueue *dst_cq = con->request_content_queue; - + int is_closed = 0; /* the connection got closed, if we don't have a complete header, -> error */ + if (con->is_readable) { con->read_idle_ts = srv->cur_ts; - + switch(connection_handle_read(srv, con)) { case -1: return -1; case -2: - /* remote side closed the connection - * if we still have content, handle it, if not leave here */ - - if (cq->first == cq->last && - cq->first->mem->used == 0) { - - /* conn-closed, leave here */ - connection_set_state(srv, con, CON_STATE_ERROR); - } + is_closed = 1; + break; default: break; } @@ -889,107 +945,103 @@ int connection_handle_read_state(server *srv, connection *con) { /* the last node was empty */ if (c->next == NULL) { cq->last = c; - } + } c = c->next; } else { c = c->next; } } - - /* nothing to handle */ - if (cq->first == NULL) return 0; + + /* we might have got several packets at once + */ switch(ostate) { case CON_STATE_READ: - /* prepare con->request.request */ - c = cq->first; - - /* check if we need the full package */ - if (con->request.request->used == 0) { - buffer b; - - b.ptr = c->mem->ptr + c->offset; - b.used = c->mem->used - c->offset; - - if (NULL != (h_term = buffer_search_rnrn(&b))) { - /* \r\n\r\n found - * - copy everything incl. the terminator to request.request - */ - - buffer_copy_string_len(con->request.request, - b.ptr, - h_term - b.ptr + 4); - - /* the buffer has been read up to the terminator */ - c->offset += h_term - b.ptr + 4; - } else { - /* not found, copy everything */ - buffer_copy_string_len(con->request.request, c->mem->ptr + c->offset, c->mem->used - c->offset - 1); - c->offset = c->mem->used - 1; - } - } else { - /* have to take care of overlapping header terminators */ - - size_t l = con->request.request->used - 2; - char *s = con->request.request->ptr; + /* if there is a \r\n\r\n in the chunkqueue + * + * scan the chunk-queue twice + * 1. to find the \r\n\r\n + * 2. to copy the header-packet + * + */ + + last_chunk = NULL; + last_offset = 0; + + for (c = cq->first; c; c = c->next) { buffer b; - + size_t i; + b.ptr = c->mem->ptr + c->offset; b.used = c->mem->used - c->offset; - - if (con->request.request->used - 1 > 3 && - c->mem->used > 1 && - s[l-2] == '\r' && - s[l-1] == '\n' && - s[l-0] == '\r' && - c->mem->ptr[0] == '\n') { - buffer_append_string_len(con->request.request, c->mem->ptr + c->offset, 1); - c->offset += 1; - - h_term = con->request.request->ptr; - } else if (con->request.request->used - 1 > 2 && - c->mem->used > 2 && - s[l-1] == '\r' && - s[l-0] == '\n' && - c->mem->ptr[0] == '\r' && - c->mem->ptr[1] == '\n') { - buffer_append_string_len(con->request.request, c->mem->ptr + c->offset, 2); - c->offset += 2; - - h_term = con->request.request->ptr; - } else if (con->request.request->used - 1 > 1 && - c->mem->used > 3 && - s[l-0] == '\r' && - c->mem->ptr[0] == '\n' && - c->mem->ptr[1] == '\r' && - c->mem->ptr[2] == '\n') { - buffer_append_string_len(con->request.request, c->mem->ptr + c->offset, 3); - c->offset += 3; - - h_term = con->request.request->ptr; - } else if (NULL != (h_term = buffer_search_string_len(&b, "\r\n\r\n", 4))) { - /* \r\n\r\n found - * - copy everything incl. the terminator to request.request - */ - - buffer_append_string_len(con->request.request, - c->mem->ptr + c->offset, - c->offset + h_term - b.ptr + 4); - - /* the buffer has been read up to the terminator */ - c->offset += h_term - b.ptr + 4; - } else { - /* not found, copy everything */ - buffer_append_string_len(con->request.request, c->mem->ptr + c->offset, c->mem->used - c->offset - 1); - c->offset = c->mem->used - 1; + if (b.used > 0) b.used--; /* buffer "used" includes terminating zero */ + + for (i = 0; i < b.used; i++) { + char ch = b.ptr[i]; + + if ('\r' == ch) { + /* chec if \n\r\n follows */ + size_t j = i+1; + chunk *cc = c; + const char header_end[] = "\r\n\r\n"; + int header_end_match_pos = 1; + + for ( ; cc; cc = cc->next, j = 0 ) { + buffer bb; + bb.ptr = cc->mem->ptr + cc->offset; + bb.used = cc->mem->used - cc->offset; + if (bb.used > 0) bb.used--; /* buffer "used" includes terminating zero */ + + for ( ; j < bb.used; j++) { + ch = bb.ptr[j]; + + if (ch == header_end[header_end_match_pos]) { + header_end_match_pos++; + if (4 == header_end_match_pos) { + last_chunk = cc; + last_offset = j+1; + goto found_header_end; + } + } else { + goto reset_search; + } + } + } + } +reset_search: ; } } +found_header_end: + + /* found */ + if (last_chunk) { + buffer_reset(con->request.request); + + for (c = cq->first; c; c = c->next) { + buffer b; + + b.ptr = c->mem->ptr + c->offset; + b.used = c->mem->used - c->offset; + + if (c == last_chunk) { + b.used = last_offset + 1; + } + + buffer_append_string_buffer(con->request.request, &b); + + if (c == last_chunk) { + c->offset += last_offset; + + break; + } else { + /* the whole packet was copied */ + c->offset = c->mem->used - 1; + } + } - /* con->request.request is setup up */ - if (h_term) { connection_set_state(srv, con, CON_STATE_REQUEST_END); - } else if (con->request.request->used > 64 * 1024) { + } else if (chunkqueue_length(cq) > 64 * 1024) { log_error_write(srv, __FILE__, __LINE__, "s", "oversized request-header -> sending Status 414"); con->http_status = 414; /* Request-URI too large */ @@ -997,16 +1049,16 @@ int connection_handle_read_state(server *srv, connection *con) { connection_set_state(srv, con, CON_STATE_HANDLE_REQUEST); } break; - case CON_STATE_READ_POST: + case CON_STATE_READ_POST: for (c = cq->first; c && (dst_cq->bytes_in != (off_t)con->request.content_length); c = c->next) { off_t weWant, weHave, toRead; - + weWant = con->request.content_length - dst_cq->bytes_in; - + assert(c->mem->used); - + weHave = c->mem->used - c->offset - 1; - + toRead = weHave > weWant ? weWant : weHave; /* the new way, copy everything into a chunkqueue whcih might use tempfiles */ @@ -1015,13 +1067,13 @@ int connection_handle_read_state(server *srv, connection *con) { /* copy everything to max 1Mb sized tempfiles */ /* - * if the last chunk is + * if the last chunk is * - smaller than 1Mb (size < 1Mb) * - not read yet (offset == 0) * -> append to it * otherwise - * -> create a new chunk - * + * -> create a new chunk + * * */ if (dst_cq->last && @@ -1036,6 +1088,9 @@ int connection_handle_read_state(server *srv, connection *con) { if (dst_c->file.fd == -1) { /* this should not happen as we cache the fd, but you never know */ dst_c->file.fd = open(dst_c->file.name->ptr, O_WRONLY | O_APPEND); +#ifdef FD_CLOEXEC + fcntl(dst_c->file.fd, F_SETFD, FD_CLOEXEC); +#endif } } else { /* the chunk is too large now, close it */ @@ -1054,14 +1109,14 @@ int connection_handle_read_state(server *srv, connection *con) { /* we have a chunk, let's write to it */ if (dst_c->file.fd == -1) { - /* we don't have file to write to, + /* we don't have file to write to, * EACCES might be one reason. * * Instead of sending 500 we send 413 and say the request is too large * */ log_error_write(srv, __FILE__, __LINE__, "sbs", - "denying upload as opening to temp-file for upload failed:", + "denying upload as opening to temp-file for upload failed:", dst_c->file.name, strerror(errno)); con->http_status = 413; /* Request-Entity too large */ @@ -1072,15 +1127,15 @@ int connection_handle_read_state(server *srv, connection *con) { } if (toRead != write(dst_c->file.fd, c->mem->ptr + c->offset, toRead)) { - /* write failed for some reason ... disk full ? */ + /* write failed for some reason ... disk full ? */ log_error_write(srv, __FILE__, __LINE__, "sbs", - "denying upload as writing to file failed:", + "denying upload as writing to file failed:", dst_c->file.name, strerror(errno)); - + con->http_status = 413; /* Request-Entity too large */ con->keep_alive = 0; connection_set_state(srv, con, CON_STATE_HANDLE_REQUEST); - + close(dst_c->file.fd); dst_c->file.fd = -1; @@ -1088,7 +1143,7 @@ int connection_handle_read_state(server *srv, connection *con) { } dst_c->file.length += toRead; - + if (dst_cq->bytes_in + toRead == (off_t)con->request.content_length) { /* we read everything, close the chunk */ close(dst_c->file.fd); @@ -1097,10 +1152,17 @@ int connection_handle_read_state(server *srv, connection *con) { } else { buffer *b; - b = chunkqueue_get_append_buffer(dst_cq); - buffer_copy_string_len(b, c->mem->ptr + c->offset, toRead); + if (dst_cq->last && + dst_cq->last->type == MEM_CHUNK) { + b = dst_cq->last->mem; + } else { + b = chunkqueue_get_append_buffer(dst_cq); + /* prepare buffer size for remaining POST data; is < 64kb */ + buffer_prepare_copy(b, con->request.content_length - dst_cq->bytes_in + 1); + } + buffer_append_string_len(b, c->mem->ptr + c->offset, toRead); } - + c->offset += toRead; dst_cq->bytes_in += toRead; } @@ -1109,8 +1171,15 @@ int connection_handle_read_state(server *srv, connection *con) { if (dst_cq->bytes_in == (off_t)con->request.content_length) { connection_set_state(srv, con, CON_STATE_HANDLE_REQUEST); } - + break; + default: break; + } + + /* the connection got closed and we didn't got enough data to leave one of the READ states + * the only way is to leave here */ + if (is_closed && ostate == con->state) { + connection_set_state(srv, con, CON_STATE_ERROR); } chunkqueue_remove_finished_chunks(cq); @@ -1118,35 +1187,39 @@ int connection_handle_read_state(server *srv, connection *con) { return 0; } -handler_t connection_handle_fdevent(void *s, void *context, int revents) { - server *srv = (server *)s; +static handler_t connection_handle_fdevent(server *srv, void *context, int revents) { connection *con = context; - + joblist_append(srv, con); - - if (revents & FDEVENT_IN) { - con->is_readable = 1; -#if 0 - log_error_write(srv, __FILE__, __LINE__, "sd", "read-wait - done", con->fd); -#endif - } - if (revents & FDEVENT_OUT) { - con->is_writable = 1; - /* we don't need the event twice */ + + if (con->conf.is_ssl) { + /* ssl may read and write for both reads and writes */ + if (revents & (FDEVENT_IN | FDEVENT_OUT)) { + con->is_readable = 1; + con->is_writable = 1; + } + } else { + if (revents & FDEVENT_IN) { + con->is_readable = 1; + } + if (revents & FDEVENT_OUT) { + con->is_writable = 1; + /* we don't need the event twice */ + } } - - + + if (revents & ~(FDEVENT_IN | FDEVENT_OUT)) { /* looks like an error */ - + /* FIXME: revents = 0x19 still means that we should read from the queue */ if (revents & FDEVENT_HUP) { if (con->state == CON_STATE_CLOSE) { - con->close_timeout_ts = 0; + con->close_timeout_ts = srv->cur_ts - (HTTP_LINGER_TIMEOUT+1); } else { /* sigio reports the wrong event here - * - * there was no HUP at all + * + * there was no HUP at all */ #ifdef USE_LINUX_SIGIO if (srv->ev->in_sigio == 1) { @@ -1158,63 +1231,50 @@ handler_t connection_handle_fdevent(void *s, void *context, int revents) { #else connection_set_state(srv, con, CON_STATE_ERROR); #endif - + } } else if (revents & FDEVENT_ERR) { -#ifndef USE_LINUX_SIGIO + /* error, connection reset, whatever... we don't want to spam the logfile */ +#if 0 log_error_write(srv, __FILE__, __LINE__, "sd", "connection closed: poll() -> ERR", con->fd); -#endif +#endif connection_set_state(srv, con, CON_STATE_ERROR); } else { log_error_write(srv, __FILE__, __LINE__, "sd", "connection closed: poll() -> ???", revents); - } + } } - + if (con->state == CON_STATE_READ || con->state == CON_STATE_READ_POST) { connection_handle_read_state(srv, con); } - + if (con->state == CON_STATE_WRITE && !chunkqueue_is_empty(con->write_queue) && con->is_writable) { - + if (-1 == connection_handle_write(srv, con)) { connection_set_state(srv, con, CON_STATE_ERROR); - + log_error_write(srv, __FILE__, __LINE__, "ds", con->fd, "handle write failed."); - } else if (con->state == CON_STATE_WRITE) { - con->write_request_ts = srv->cur_ts; } } - + if (con->state == CON_STATE_CLOSE) { /* flush the read buffers */ - int b; - - if (ioctl(con->fd, FIONREAD, &b)) { - log_error_write(srv, __FILE__, __LINE__, "ss", - "ioctl() failed", strerror(errno)); - } - - if (b > 0) { - char buf[1024]; - log_error_write(srv, __FILE__, __LINE__, "sdd", - "CLOSE-read()", con->fd, b); - - /* */ - read(con->fd, buf, sizeof(buf)); - } else { - /* nothing to read */ - - con->close_timeout_ts = 0; + int len; + char buf[1024]; + + len = read(con->fd, buf, sizeof(buf)); + if (len == 0 || (len < 0 && errno != EAGAIN && errno != EINTR) ) { + con->close_timeout_ts = srv->cur_ts - (HTTP_LINGER_TIMEOUT+1); } } - + return HANDLER_FINISHED; } @@ -1227,43 +1287,65 @@ connection *connection_accept(server *srv, server_socket *srv_socket) { sock_addr cnt_addr; socklen_t cnt_len; /* accept it and register the fd */ - + + /** + * check if we can still open a new connections + * + * see #1216 + */ + + if (srv->conns->used >= srv->max_conns) { + return NULL; + } + cnt_len = sizeof(cnt_addr); if (-1 == (cnt = accept(srv_socket->fd, (struct sockaddr *) &cnt_addr, &cnt_len))) { - if ((errno != EAGAIN) && - (errno != EINTR)) { + switch (errno) { + case EAGAIN: +#if EWOULDBLOCK != EAGAIN + case EWOULDBLOCK: +#endif + case EINTR: + /* we were stopped _before_ we had a connection */ + case ECONNABORTED: /* this is a FreeBSD thingy */ + /* we were stopped _after_ we had a connection */ + break; + case EMFILE: + /* out of fds */ + break; + default: log_error_write(srv, __FILE__, __LINE__, "ssd", "accept failed:", strerror(errno), errno); } return NULL; } else { connection *con; - + srv->cur_fds++; - + /* ok, we have the connection, register it */ #if 0 log_error_write(srv, __FILE__, __LINE__, "sd", "appected()", cnt); #endif srv->con_opened++; - + con = connections_get_new_connection(srv); - + con->fd = cnt; con->fde_ndx = -1; -#if 0 +#if 0 gettimeofday(&(con->start_tv), NULL); -#endif +#endif fdevent_register(srv->ev, con->fd, connection_handle_fdevent, con); - + connection_set_state(srv, con, CON_STATE_REQUEST_START); - + con->connection_start = srv->cur_ts; con->dst_addr = cnt_addr; buffer_copy_string(con->dst_addr_buf, inet_ntop_cache_get_ip(srv, &(con->dst_addr))); con->srv_socket = srv_socket; - + if (-1 == (fdevent_fcntl_set(srv->ev, con->fd))) { log_error_write(srv, __FILE__, __LINE__, "ss", "fcntl failed: ", strerror(errno)); return NULL; @@ -1272,17 +1354,19 @@ connection *connection_accept(server *srv, server_socket *srv_socket) { /* connect FD to SSL */ if (srv_socket->is_ssl) { if (NULL == (con->ssl = SSL_new(srv_socket->ssl_ctx))) { - log_error_write(srv, __FILE__, __LINE__, "ss", "SSL:", + log_error_write(srv, __FILE__, __LINE__, "ss", "SSL:", ERR_error_string(ERR_get_error(), NULL)); - + return NULL; } - + + con->renegotiations = 0; + SSL_set_app_data(con->ssl, con); SSL_set_accept_state(con->ssl); con->conf.is_ssl=1; - + if (1 != (SSL_set_fd(con->ssl, cnt))) { - log_error_write(srv, __FILE__, __LINE__, "ss", "SSL:", + log_error_write(srv, __FILE__, __LINE__, "ss", "SSL:", ERR_error_string(ERR_get_error(), NULL)); return NULL; } @@ -1298,129 +1382,133 @@ int connection_state_machine(server *srv, connection *con) { #ifdef USE_OPENSSL server_socket *srv_sock = con->srv_socket; #endif - + if (srv->srvconf.log_state_handling) { - log_error_write(srv, __FILE__, __LINE__, "sds", - "state at start", + log_error_write(srv, __FILE__, __LINE__, "sds", + "state at start", con->fd, connection_get_state(con->state)); } while (done == 0) { size_t ostate = con->state; - int b; - + switch (con->state) { case CON_STATE_REQUEST_START: /* transient */ if (srv->srvconf.log_state_handling) { - log_error_write(srv, __FILE__, __LINE__, "sds", + log_error_write(srv, __FILE__, __LINE__, "sds", "state for fd", con->fd, connection_get_state(con->state)); } - + con->request_start = srv->cur_ts; con->read_idle_ts = srv->cur_ts; - + con->request_count++; con->loops_per_request = 0; - + connection_set_state(srv, con, CON_STATE_READ); - + + /* patch con->conf.is_ssl if the connection is a ssl-socket already */ + +#ifdef USE_OPENSSL + con->conf.is_ssl = srv_sock->is_ssl; +#endif + break; case CON_STATE_REQUEST_END: /* transient */ if (srv->srvconf.log_state_handling) { - log_error_write(srv, __FILE__, __LINE__, "sds", + log_error_write(srv, __FILE__, __LINE__, "sds", "state for fd", con->fd, connection_get_state(con->state)); } - + + buffer_reset(con->uri.authority); + buffer_reset(con->uri.path); + buffer_reset(con->uri.query); + buffer_reset(con->request.orig_uri); + if (http_request_parse(srv, con)) { /* we have to read some data from the POST request */ - + connection_set_state(srv, con, CON_STATE_READ_POST); break; } - + connection_set_state(srv, con, CON_STATE_HANDLE_REQUEST); - + break; case CON_STATE_HANDLE_REQUEST: - /* + /* * the request is parsed - * + * * decided what to do with the request - * - - * - * + * - + * + * */ - + if (srv->srvconf.log_state_handling) { - log_error_write(srv, __FILE__, __LINE__, "sds", + log_error_write(srv, __FILE__, __LINE__, "sds", "state for fd", con->fd, connection_get_state(con->state)); } - + switch (r = http_response_prepare(srv, con)) { case HANDLER_FINISHED: - if (con->http_status == 404 || - con->http_status == 403) { - /* 404 error-handler */ - - if (con->in_error_handler == 0 && - (!buffer_is_empty(con->conf.error_handler) || - !buffer_is_empty(con->error_handler))) { - /* call error-handler */ - - con->error_handler_saved_status = con->http_status; - con->http_status = 0; - - if (buffer_is_empty(con->error_handler)) { - buffer_copy_string_buffer(con->request.uri, con->conf.error_handler); - } else { - buffer_copy_string_buffer(con->request.uri, con->error_handler); + if (con->mode == DIRECT) { + if (con->http_status == 404 || + con->http_status == 403) { + /* 404 error-handler */ + + if (con->in_error_handler == 0 && + (!buffer_is_empty(con->conf.error_handler) || + !buffer_is_empty(con->error_handler))) { + /* call error-handler */ + + con->error_handler_saved_status = con->http_status; + con->http_status = 0; + + if (buffer_is_empty(con->error_handler)) { + buffer_copy_string_buffer(con->request.uri, con->conf.error_handler); + } else { + buffer_copy_string_buffer(con->request.uri, con->error_handler); + } + buffer_reset(con->physical.path); + + con->in_error_handler = 1; + + connection_set_state(srv, con, CON_STATE_HANDLE_REQUEST); + + done = -1; + break; + } else if (con->in_error_handler) { + /* error-handler is a 404 */ + + con->http_status = con->error_handler_saved_status; } - buffer_reset(con->physical.path); - - con->in_error_handler = 1; - - connection_set_state(srv, con, CON_STATE_HANDLE_REQUEST); - - done = -1; - break; } else if (con->in_error_handler) { - /* error-handler is a 404 */ - - /* continue as normal, status is the same */ - log_error_write(srv, __FILE__, __LINE__, "sb", - "Warning: Either the error-handler returned status 404 or the error-handler itself was not found:", con->request.uri); - log_error_write(srv, __FILE__, __LINE__, "sd", - "returning the original status", con->error_handler_saved_status); - log_error_write(srv, __FILE__, __LINE__, "s", - "If this is a rails app: check your production.log"); - con->http_status = con->error_handler_saved_status; + /* error-handler is back and has generated content */ + /* if Status: was set, take it otherwise use 200 */ } - } else if (con->in_error_handler) { - /* error-handler is back and has generated content */ - /* if Status: was set, take it otherwise use 200 */ } - if (con->http_status == 0) con->http_status = 200; - + /* we have something to send, go on */ connection_set_state(srv, con, CON_STATE_RESPONSE_START); break; case HANDLER_WAIT_FOR_FD: srv->want_fds++; - + fdwaitqueue_append(srv, con); - + connection_set_state(srv, con, CON_STATE_HANDLE_REQUEST); - + break; case HANDLER_COMEBACK: done = -1; case HANDLER_WAIT_FOR_EVENT: /* come back here */ connection_set_state(srv, con, CON_STATE_HANDLE_REQUEST); - + break; case HANDLER_ERROR: /* something went wrong */ @@ -1430,44 +1518,44 @@ int connection_state_machine(server *srv, connection *con) { log_error_write(srv, __FILE__, __LINE__, "sdd", "unknown ret-value: ", con->fd, r); break; } - + break; case CON_STATE_RESPONSE_START: - /* + /* * the decision is done * - create the HTTP-Response-Header - * + * */ - + if (srv->srvconf.log_state_handling) { - log_error_write(srv, __FILE__, __LINE__, "sds", + log_error_write(srv, __FILE__, __LINE__, "sds", "state for fd", con->fd, connection_get_state(con->state)); } - + if (-1 == connection_handle_write_prepare(srv, con)) { connection_set_state(srv, con, CON_STATE_ERROR); - + break; } - + connection_set_state(srv, con, CON_STATE_WRITE); break; case CON_STATE_RESPONSE_END: /* transient */ /* log the request */ - + if (srv->srvconf.log_state_handling) { - log_error_write(srv, __FILE__, __LINE__, "sds", + log_error_write(srv, __FILE__, __LINE__, "sds", "state for fd", con->fd, connection_get_state(con->state)); } - + plugins_call_handle_request_done(srv, con); - + srv->con_written++; - + if (con->keep_alive) { connection_set_state(srv, con, CON_STATE_REQUEST_START); - -#if 0 + +#if 0 con->request_start = srv->cur_ts; con->read_idle_ts = srv->cur_ts; #endif @@ -1480,7 +1568,7 @@ int connection_state_machine(server *srv, connection *con) { log_error_write(srv, __FILE__, __LINE__, "sd", "unhandling return value", r); break; } - + #ifdef USE_OPENSSL if (srv_sock->is_ssl) { switch (SSL_shutdown(con->ssl)) { @@ -1488,90 +1576,89 @@ int connection_state_machine(server *srv, connection *con) { /* done */ break; case 0: - /* wait for fd-event - * + /* wait for fd-event + * * FIXME: wait for fdevent and call SSL_shutdown again - * + * */ - + break; default: - log_error_write(srv, __FILE__, __LINE__, "ss", "SSL:", + log_error_write(srv, __FILE__, __LINE__, "ss", "SSL:", ERR_error_string(ERR_get_error(), NULL)); } } #endif - connection_close(srv, con); - + if ((0 == shutdown(con->fd, SHUT_WR))) { + con->close_timeout_ts = srv->cur_ts; + connection_set_state(srv, con, CON_STATE_CLOSE); + } else { + connection_close(srv, con); + } + srv->con_closed++; } - + connection_reset(srv, con); - + break; case CON_STATE_CONNECT: if (srv->srvconf.log_state_handling) { - log_error_write(srv, __FILE__, __LINE__, "sds", + log_error_write(srv, __FILE__, __LINE__, "sds", "state for fd", con->fd, connection_get_state(con->state)); } - + chunkqueue_reset(con->read_queue); - + con->request_count = 0; - + break; case CON_STATE_CLOSE: if (srv->srvconf.log_state_handling) { - log_error_write(srv, __FILE__, __LINE__, "sds", + log_error_write(srv, __FILE__, __LINE__, "sds", "state for fd", con->fd, connection_get_state(con->state)); } - - if (con->keep_alive) { - if (ioctl(con->fd, FIONREAD, &b)) { - log_error_write(srv, __FILE__, __LINE__, "ss", - "ioctl() failed", strerror(errno)); - } - if (b > 0) { - char buf[1024]; - log_error_write(srv, __FILE__, __LINE__, "sdd", - "CLOSE-read()", con->fd, b); - - /* */ - read(con->fd, buf, sizeof(buf)); - } else { - /* nothing to read */ - - con->close_timeout_ts = 0; + + /* we have to do the linger_on_close stuff regardless + * of con->keep_alive; even non-keepalive sockets may + * still have unread data, and closing before reading + * it will make the client not see all our output. + */ + { + int len; + char buf[1024]; + + len = read(con->fd, buf, sizeof(buf)); + if (len == 0 || (len < 0 && errno != EAGAIN && errno != EINTR) ) { + con->close_timeout_ts = srv->cur_ts - (HTTP_LINGER_TIMEOUT+1); } - } else { - con->close_timeout_ts = 0; } - - if (srv->cur_ts - con->close_timeout_ts > 1) { + + if (srv->cur_ts - con->close_timeout_ts > HTTP_LINGER_TIMEOUT) { connection_close(srv, con); - + if (srv->srvconf.log_state_handling) { - log_error_write(srv, __FILE__, __LINE__, "sd", + log_error_write(srv, __FILE__, __LINE__, "sd", "connection closed for fd", con->fd); } } - + break; case CON_STATE_READ_POST: case CON_STATE_READ: if (srv->srvconf.log_state_handling) { - log_error_write(srv, __FILE__, __LINE__, "sds", + log_error_write(srv, __FILE__, __LINE__, "sds", "state for fd", con->fd, connection_get_state(con->state)); } - + connection_handle_read_state(srv, con); break; case CON_STATE_WRITE: if (srv->srvconf.log_state_handling) { - log_error_write(srv, __FILE__, __LINE__, "sds", + log_error_write(srv, __FILE__, __LINE__, "sds", "state for fd", con->fd, connection_get_state(con->state)); } - + /* only try to write if we have something in the queue */ if (!chunkqueue_is_empty(con->write_queue)) { #if 0 @@ -1587,42 +1674,77 @@ int connection_state_machine(server *srv, connection *con) { con->fd, "handle write failed."); connection_set_state(srv, con, CON_STATE_ERROR); - } else if (con->state == CON_STATE_WRITE) { - con->write_request_ts = srv->cur_ts; } } - + break; case CON_STATE_ERROR: /* transient */ - + /* even if the connection was drop we still have to write it to the access log */ if (con->http_status) { plugins_call_handle_request_done(srv, con); } #ifdef USE_OPENSSL if (srv_sock->is_ssl) { - int ret; + int ret, ssl_r; + unsigned long err; + ERR_clear_error(); switch ((ret = SSL_shutdown(con->ssl))) { case 1: /* ok */ break; case 0: - SSL_shutdown(con->ssl); - break; + ERR_clear_error(); + if (-1 != (ret = SSL_shutdown(con->ssl))) break; + + /* fall through */ default: - log_error_write(srv, __FILE__, __LINE__, "sds", "SSL:", - SSL_get_error(con->ssl, ret), - ERR_error_string(ERR_get_error(), NULL)); - return -1; + + switch ((ssl_r = SSL_get_error(con->ssl, ret))) { + case SSL_ERROR_WANT_WRITE: + case SSL_ERROR_WANT_READ: + break; + case SSL_ERROR_SYSCALL: + /* perhaps we have error waiting in our error-queue */ + if (0 != (err = ERR_get_error())) { + do { + log_error_write(srv, __FILE__, __LINE__, "sdds", "SSL:", + ssl_r, ret, + ERR_error_string(err, NULL)); + } while((err = ERR_get_error())); + } else if (errno != 0) { /* ssl bug (see lighttpd ticket #2213): sometimes errno == 0 */ + switch(errno) { + case EPIPE: + case ECONNRESET: + break; + default: + log_error_write(srv, __FILE__, __LINE__, "sddds", "SSL (error):", + ssl_r, ret, errno, + strerror(errno)); + break; + } + } + + break; + default: + while((err = ERR_get_error())) { + log_error_write(srv, __FILE__, __LINE__, "sdds", "SSL:", + ssl_r, ret, + ERR_error_string(err, NULL)); + } + + break; + } } } + ERR_clear_error(); #endif - + switch(con->mode) { case DIRECT: #if 0 - log_error_write(srv, __FILE__, __LINE__, "sd", - "emergency exit: direct", + log_error_write(srv, __FILE__, __LINE__, "sd", + "emergency exit: direct", con->fd); #endif break; @@ -1632,40 +1754,39 @@ int connection_state_machine(server *srv, connection *con) { case HANDLER_FINISHED: break; default: - log_error_write(srv, __FILE__, __LINE__, ""); + log_error_write(srv, __FILE__, __LINE__, "sd", "unhandling return value", r); break; } break; } - + connection_reset(srv, con); - + /* close the connection */ - if ((con->keep_alive == 1) && - (0 == shutdown(con->fd, SHUT_WR))) { + if ((0 == shutdown(con->fd, SHUT_WR))) { con->close_timeout_ts = srv->cur_ts; connection_set_state(srv, con, CON_STATE_CLOSE); - + if (srv->srvconf.log_state_handling) { - log_error_write(srv, __FILE__, __LINE__, "sd", + log_error_write(srv, __FILE__, __LINE__, "sd", "shutdown for fd", con->fd); } } else { connection_close(srv, con); } - + con->keep_alive = 0; - + srv->con_closed++; - + break; default: - log_error_write(srv, __FILE__, __LINE__, "sdd", + log_error_write(srv, __FILE__, __LINE__, "sdd", "unknown state:", con->fd, con->state); - + break; } - + if (done == -1) { done = 0; } else if (ostate == con->state) { @@ -1674,27 +1795,27 @@ int connection_state_machine(server *srv, connection *con) { } if (srv->srvconf.log_state_handling) { - log_error_write(srv, __FILE__, __LINE__, "sds", - "state at exit:", + log_error_write(srv, __FILE__, __LINE__, "sds", + "state at exit:", con->fd, connection_get_state(con->state)); } - + switch(con->state) { case CON_STATE_READ_POST: case CON_STATE_READ: case CON_STATE_CLOSE: - fdevent_event_add(srv->ev, &(con->fde_ndx), con->fd, FDEVENT_IN); + fdevent_event_set(srv->ev, &(con->fde_ndx), con->fd, FDEVENT_IN); break; case CON_STATE_WRITE: - /* request write-fdevent only if we really need it + /* request write-fdevent only if we really need it * - if we have data to write - * - if the socket is not writable yet + * - if the socket is not writable yet */ - if (!chunkqueue_is_empty(con->write_queue) && + if (!chunkqueue_is_empty(con->write_queue) && (con->is_writable == 0) && (con->traffic_limit_reached == 0)) { - fdevent_event_add(srv->ev, &(con->fde_ndx), con->fd, FDEVENT_OUT); + fdevent_event_set(srv->ev, &(con->fde_ndx), con->fd, FDEVENT_OUT); } else { fdevent_event_del(srv->ev, &(con->fde_ndx), con->fd); } diff --git a/src/crc32.h b/src/crc32.h index d671c53..c5b4245 100644 --- a/src/crc32.h +++ b/src/crc32.h @@ -2,15 +2,15 @@ #define __crc32cr_table_h__ #ifdef HAVE_CONFIG_H -#include "config.h" +# include "config.h" #endif #include <sys/types.h> #if defined HAVE_STDINT_H -#include <stdint.h> +# include <stdint.h> #elif defined HAVE_INTTYPES_H -#include <inttypes.h> +# include <inttypes.h> #endif uint32_t generate_crc32c(char *string, size_t length); diff --git a/src/data_array.c b/src/data_array.c index 9dfa6fd..094d8c0 100644 --- a/src/data_array.c +++ b/src/data_array.c @@ -1,9 +1,9 @@ +#include "array.h" + #include <string.h> #include <stdio.h> #include <stdlib.h> -#include "array.h" - static data_unset *data_array_copy(const data_unset *s) { data_array *src = (data_array *)s; data_array *ds = data_array_init(); @@ -17,16 +17,16 @@ static data_unset *data_array_copy(const data_unset *s) { static void data_array_free(data_unset *d) { data_array *ds = (data_array *)d; - + buffer_free(ds->key); array_free(ds->value); - + free(d); } static void data_array_reset(data_unset *d) { data_array *ds = (data_array *)d; - + /* reused array elements */ buffer_reset(ds->key); array_reset(ds->value); @@ -36,7 +36,7 @@ static int data_array_insert_dup(data_unset *dst, data_unset *src) { UNUSED(dst); src->free(src); - + return 0; } @@ -48,18 +48,18 @@ static void data_array_print(const data_unset *d, int depth) { data_array *data_array_init(void) { data_array *ds; - + ds = calloc(1, sizeof(*ds)); - + ds->key = buffer_init(); ds->value = array_init(); - + ds->copy = data_array_copy; ds->free = data_array_free; ds->reset = data_array_reset; ds->insert_dup = data_array_insert_dup; ds->print = data_array_print; ds->type = TYPE_ARRAY; - + return ds; } diff --git a/src/data_config.c b/src/data_config.c index 8b3735e..80e38de 100644 --- a/src/data_config.c +++ b/src/data_config.c @@ -1,9 +1,9 @@ +#include "array.h" + #include <string.h> #include <stdio.h> #include <stdlib.h> -#include "array.h" - static data_unset *data_config_copy(const data_unset *s) { data_config *src = (data_config *)s; data_config *ds = data_config_init(); @@ -17,26 +17,26 @@ static data_unset *data_config_copy(const data_unset *s) { static void data_config_free(data_unset *d) { data_config *ds = (data_config *)d; - + buffer_free(ds->key); buffer_free(ds->op); buffer_free(ds->comp_key); - + array_free(ds->value); array_free(ds->childs); - + if (ds->string) buffer_free(ds->string); #ifdef HAVE_PCRE_H if (ds->regex) pcre_free(ds->regex); if (ds->regex_study) pcre_free(ds->regex_study); #endif - + free(d); } static void data_config_reset(data_unset *d) { data_config *ds = (data_config *)d; - + /* reused array elements */ buffer_reset(ds->key); buffer_reset(ds->comp_key); @@ -45,9 +45,9 @@ static void data_config_reset(data_unset *d) { static int data_config_insert_dup(data_unset *dst, data_unset *src) { UNUSED(dst); - + src->free(src); - + return 0; } @@ -56,15 +56,15 @@ static void data_config_print(const data_unset *d, int depth) { array *a = (array *)ds->value; size_t i; size_t maxlen; - + if (0 == ds->context_ndx) { - fprintf(stderr, "config {\n"); + fprintf(stdout, "config {\n"); } else { - fprintf(stderr, "$%s %s \"%s\" {\n", + fprintf(stdout, "$%s %s \"%s\" {\n", ds->comp_key->ptr, ds->op->ptr, ds->string->ptr); array_print_indent(depth + 1); - fprintf(stderr, "# block %d\n", ds->context_ndx); + fprintf(stdout, "# block %d\n", ds->context_ndx); } depth ++; @@ -75,64 +75,64 @@ static void data_config_print(const data_unset *d, int depth) { size_t j; array_print_indent(depth); - fprintf(stderr, "%s", du->key->ptr); + fprintf(stdout, "%s", du->key->ptr); for (j = maxlen - len; j > 0; j --) { - fprintf(stderr, " "); + fprintf(stdout, " "); } - fprintf(stderr, " = "); + fprintf(stdout, " = "); du->print(du, depth); - fprintf(stderr, "\n"); + fprintf(stdout, "\n"); } if (ds->childs) { - fprintf(stderr, "\n"); + fprintf(stdout, "\n"); for (i = 0; i < ds->childs->used; i ++) { data_unset *du = ds->childs->data[i]; /* only the 1st block of chaining */ if (NULL == ((data_config *)du)->prev) { - fprintf(stderr, "\n"); + fprintf(stdout, "\n"); array_print_indent(depth); du->print(du, depth); - fprintf(stderr, "\n"); + fprintf(stdout, "\n"); } } } depth --; array_print_indent(depth); - fprintf(stderr, "}"); + fprintf(stdout, "}"); if (0 != ds->context_ndx) { - fprintf(stderr, " # end of $%s %s \"%s\"", + fprintf(stdout, " # end of $%s %s \"%s\"", ds->comp_key->ptr, ds->op->ptr, ds->string->ptr); } if (ds->next) { - fprintf(stderr, "\n"); + fprintf(stdout, "\n"); array_print_indent(depth); - fprintf(stderr, "else "); + fprintf(stdout, "else "); ds->next->print((data_unset *)ds->next, depth); } } data_config *data_config_init(void) { data_config *ds; - + ds = calloc(1, sizeof(*ds)); - + ds->key = buffer_init(); ds->op = buffer_init(); ds->comp_key = buffer_init(); ds->value = array_init(); ds->childs = array_init(); ds->childs->is_weakref = 1; - + ds->copy = data_config_copy; ds->free = data_config_free; ds->reset = data_config_reset; ds->insert_dup = data_config_insert_dup; ds->print = data_config_print; ds->type = TYPE_CONFIG; - + return ds; } diff --git a/src/data_count.c b/src/data_count.c index ca51f67..8d36c8b 100644 --- a/src/data_count.c +++ b/src/data_count.c @@ -1,9 +1,9 @@ +#include "array.h" + #include <string.h> #include <stdio.h> #include <stdlib.h> -#include "array.h" - static data_unset *data_count_copy(const data_unset *s) { data_count *src = (data_count *)s; data_count *ds = data_count_init(); @@ -16,53 +16,53 @@ static data_unset *data_count_copy(const data_unset *s) { static void data_count_free(data_unset *d) { data_count *ds = (data_count *)d; - + buffer_free(ds->key); - + free(d); } static void data_count_reset(data_unset *d) { data_count *ds = (data_count *)d; - + buffer_reset(ds->key); - + ds->count = 0; } static int data_count_insert_dup(data_unset *dst, data_unset *src) { data_count *ds_dst = (data_count *)dst; data_count *ds_src = (data_count *)src; - + ds_dst->count += ds_src->count; - + src->free(src); - + return 0; } static void data_count_print(const data_unset *d, int depth) { data_count *ds = (data_count *)d; UNUSED(depth); - - fprintf(stderr, "count(%d)", ds->count); + + fprintf(stdout, "count(%d)", ds->count); } data_count *data_count_init(void) { data_count *ds; - + ds = calloc(1, sizeof(*ds)); - + ds->key = buffer_init(); ds->count = 1; - + ds->copy = data_count_copy; ds->free = data_count_free; ds->reset = data_count_reset; ds->insert_dup = data_count_insert_dup; ds->print = data_count_print; ds->type = TYPE_COUNT; - + return ds; } diff --git a/src/data_fastcgi.c b/src/data_fastcgi.c index 714b290..e13a470 100644 --- a/src/data_fastcgi.c +++ b/src/data_fastcgi.c @@ -1,10 +1,10 @@ +#include "array.h" +#include "fastcgi.h" + #include <string.h> #include <stdio.h> #include <stdlib.h> -#include "array.h" -#include "fastcgi.h" - static data_unset *data_fastcgi_copy(const data_unset *s) { data_fastcgi *src = (data_fastcgi *)s; data_fastcgi *ds = data_fastcgi_init(); @@ -17,53 +17,53 @@ static data_unset *data_fastcgi_copy(const data_unset *s) { static void data_fastcgi_free(data_unset *d) { data_fastcgi *ds = (data_fastcgi *)d; - + buffer_free(ds->key); buffer_free(ds->host); - + free(d); } static void data_fastcgi_reset(data_unset *d) { data_fastcgi *ds = (data_fastcgi *)d; - + buffer_reset(ds->key); buffer_reset(ds->host); - + } static int data_fastcgi_insert_dup(data_unset *dst, data_unset *src) { UNUSED(dst); src->free(src); - + return 0; } static void data_fastcgi_print(const data_unset *d, int depth) { data_fastcgi *ds = (data_fastcgi *)d; UNUSED(depth); - - fprintf(stderr, "fastcgi(%s)", ds->host->ptr); + + fprintf(stdout, "fastcgi(%s)", ds->host->ptr); } data_fastcgi *data_fastcgi_init(void) { data_fastcgi *ds; - + ds = calloc(1, sizeof(*ds)); - + ds->key = buffer_init(); ds->host = buffer_init(); ds->port = 0; ds->is_disabled = 0; - + ds->copy = data_fastcgi_copy; ds->free = data_fastcgi_free; ds->reset = data_fastcgi_reset; ds->insert_dup = data_fastcgi_insert_dup; ds->print = data_fastcgi_print; ds->type = TYPE_FASTCGI; - + return ds; } diff --git a/src/data_integer.c b/src/data_integer.c index 96d1d0a..63cbb10 100644 --- a/src/data_integer.c +++ b/src/data_integer.c @@ -1,9 +1,9 @@ +#include "array.h" + #include <stdio.h> #include <stdlib.h> #include <string.h> -#include "array.h" - static data_unset *data_integer_copy(const data_unset *s) { data_integer *src = (data_integer *)s; data_integer *ds = data_integer_init(); @@ -16,15 +16,15 @@ static data_unset *data_integer_copy(const data_unset *s) { static void data_integer_free(data_unset *d) { data_integer *ds = (data_integer *)d; - + buffer_free(ds->key); - + free(d); } static void data_integer_reset(data_unset *d) { data_integer *ds = (data_integer *)d; - + /* reused integer elements */ buffer_reset(ds->key); ds->value = 0; @@ -32,9 +32,9 @@ static void data_integer_reset(data_unset *d) { static int data_integer_insert_dup(data_unset *dst, data_unset *src) { UNUSED(dst); - + src->free(src); - + return 0; } @@ -42,24 +42,24 @@ static void data_integer_print(const data_unset *d, int depth) { data_integer *ds = (data_integer *)d; UNUSED(depth); - fprintf(stderr, "%d", ds->value); + fprintf(stdout, "%d", ds->value); } data_integer *data_integer_init(void) { data_integer *ds; - + ds = calloc(1, sizeof(*ds)); - + ds->key = buffer_init(); ds->value = 0; - + ds->copy = data_integer_copy; ds->free = data_integer_free; ds->reset = data_integer_reset; ds->insert_dup = data_integer_insert_dup; ds->print = data_integer_print; ds->type = TYPE_INTEGER; - + return ds; } diff --git a/src/data_string.c b/src/data_string.c index d9325da..7648946 100644 --- a/src/data_string.c +++ b/src/data_string.c @@ -1,10 +1,10 @@ +#include "array.h" + #include <string.h> #include <stdio.h> #include <stdlib.h> #include <assert.h> -#include "array.h" - static data_unset *data_string_copy(const data_unset *s) { data_string *src = (data_string *)s; data_string *ds = data_string_init(); @@ -17,16 +17,16 @@ static data_unset *data_string_copy(const data_unset *s) { static void data_string_free(data_unset *d) { data_string *ds = (data_string *)d; - + buffer_free(ds->key); buffer_free(ds->value); - + free(d); } static void data_string_reset(data_unset *d) { data_string *ds = (data_string *)d; - + /* reused array elements */ buffer_reset(ds->key); buffer_reset(ds->value); @@ -35,70 +35,87 @@ static void data_string_reset(data_unset *d) { static int data_string_insert_dup(data_unset *dst, data_unset *src) { data_string *ds_dst = (data_string *)dst; data_string *ds_src = (data_string *)src; - + if (ds_dst->value->used) { - buffer_append_string(ds_dst->value, ", "); + buffer_append_string_len(ds_dst->value, CONST_STR_LEN(", ")); buffer_append_string_buffer(ds_dst->value, ds_src->value); } else { buffer_copy_string_buffer(ds_dst->value, ds_src->value); } - + src->free(src); - + return 0; } static int data_response_insert_dup(data_unset *dst, data_unset *src) { data_string *ds_dst = (data_string *)dst; data_string *ds_src = (data_string *)src; - + if (ds_dst->value->used) { - buffer_append_string(ds_dst->value, "\r\n"); + buffer_append_string_len(ds_dst->value, CONST_STR_LEN("\r\n")); buffer_append_string_buffer(ds_dst->value, ds_dst->key); - buffer_append_string(ds_dst->value, ": "); + buffer_append_string_len(ds_dst->value, CONST_STR_LEN(": ")); buffer_append_string_buffer(ds_dst->value, ds_src->value); } else { buffer_copy_string_buffer(ds_dst->value, ds_src->value); } - + src->free(src); - + return 0; } static void data_string_print(const data_unset *d, int depth) { data_string *ds = (data_string *)d; + unsigned int i; UNUSED(depth); - fprintf(stderr, "\"%s\"", ds->value->used ? ds->value->ptr : ""); + /* empty and uninitialized strings */ + if (ds->value->used < 1) { + fputs("\"\"", stdout); + return; + } + + /* print out the string as is, except prepend " with backslash */ + putc('"', stdout); + for (i = 0; i < ds->value->used - 1; i++) { + unsigned char c = ds->value->ptr[i]; + if (c == '"') { + fputs("\\\"", stdout); + } else { + putc(c, stdout); + } + } + putc('"', stdout); } data_string *data_string_init(void) { data_string *ds; - + ds = calloc(1, sizeof(*ds)); assert(ds); - + ds->key = buffer_init(); ds->value = buffer_init(); - + ds->copy = data_string_copy; ds->free = data_string_free; ds->reset = data_string_reset; ds->insert_dup = data_string_insert_dup; ds->print = data_string_print; ds->type = TYPE_STRING; - + return ds; } data_string *data_response_init(void) { data_string *ds; - + ds = data_string_init(); ds->insert_dup = data_response_insert_dup; - + return ds; } @@ -1,32 +1,51 @@ -#include <string.h> - #include "buffer.h" #include "etag.h" +#if defined HAVE_STDINT_H +# include <stdint.h> +#elif defined HAVE_INTTYPES_H +# include <inttypes.h> +#endif + +#include <string.h> + int etag_is_equal(buffer *etag, const char *matches) { - if (0 == strcmp(etag->ptr, matches)) return 1; + if (etag && !buffer_is_empty(etag) && 0 == strcmp(etag->ptr, matches)) return 1; return 0; } -int etag_create(buffer *etag, struct stat *st) { - buffer_copy_off_t(etag, st->st_ino); - buffer_append_string_len(etag, CONST_STR_LEN("-")); - buffer_append_off_t(etag, st->st_size); - buffer_append_string_len(etag, CONST_STR_LEN("-")); - buffer_append_long(etag, st->st_mtime); +int etag_create(buffer *etag, struct stat *st,etag_flags_t flags) { + if (0 == flags) return 0; + + buffer_reset(etag); + + if (flags & ETAG_USE_INODE) { + buffer_append_off_t(etag, st->st_ino); + buffer_append_string_len(etag, CONST_STR_LEN("-")); + } + + if (flags & ETAG_USE_SIZE) { + buffer_append_off_t(etag, st->st_size); + buffer_append_string_len(etag, CONST_STR_LEN("-")); + } + if (flags & ETAG_USE_MTIME) { + buffer_append_long(etag, st->st_mtime); + } + return 0; } int etag_mutate(buffer *mut, buffer *etag) { - size_t h, i; - - for (h=0, i=0; i < etag->used; ++i) h = (h<<5)^(h>>27)^(etag->ptr[i]); - + size_t i; + uint32_t h; + + for (h=0, i=0; i < etag->used-1; ++i) h = (h<<5)^(h>>27)^(etag->ptr[i]); + buffer_reset(mut); buffer_copy_string_len(mut, CONST_STR_LEN("\"")); - buffer_append_long(mut, h); + buffer_append_off_t(mut, h); buffer_append_string_len(mut, CONST_STR_LEN("\"")); - + return 0; } @@ -1,15 +1,17 @@ #ifndef ETAG_H #define ETAG_H +#include "buffer.h" + #include <sys/types.h> #include <sys/stat.h> #include <unistd.h> -#include "buffer.h" +typedef enum { ETAG_USE_INODE = 1, ETAG_USE_MTIME = 2, ETAG_USE_SIZE = 4 } etag_flags_t; int etag_is_equal(buffer *etag, const char *matches); -int etag_create(buffer *etag, struct stat *st); +int etag_create(buffer *etag, struct stat *st, etag_flags_t flags); int etag_mutate(buffer *mut, buffer *etag); - + #endif diff --git a/src/fastcgi.h b/src/fastcgi.h index 15f1dea..31c00ad 100644 --- a/src/fastcgi.h +++ b/src/fastcgi.h @@ -1,4 +1,4 @@ -/* +/* * fastcgi.h -- * * Defines for the FastCGI protocol. @@ -123,7 +123,7 @@ typedef struct { typedef struct { - unsigned char type; + unsigned char type; unsigned char reserved[7]; } FCGI_UnknownTypeBody; diff --git a/src/fdevent.c b/src/fdevent.c index fdf834f..fdd178d 100644 --- a/src/fdevent.c +++ b/src/fdevent.c @@ -1,6 +1,7 @@ -#include <sys/types.h> +#include "base.h" +#include "log.h" -#include "settings.h" +#include <sys/types.h> #include <unistd.h> #include <stdlib.h> @@ -8,110 +9,119 @@ #include <errno.h> #include <stdio.h> #include <fcntl.h> +#include <assert.h> -#include "fdevent.h" -#include "buffer.h" -fdevents *fdevent_init(size_t maxfds, fdevent_handler_t type) { +fdevents *fdevent_init(server *srv, size_t maxfds, fdevent_handler_t type) { fdevents *ev; - + ev = calloc(1, sizeof(*ev)); + ev->srv = srv; ev->fdarray = calloc(maxfds, sizeof(*ev->fdarray)); ev->maxfds = maxfds; - + switch(type) { case FDEVENT_HANDLER_POLL: if (0 != fdevent_poll_init(ev)) { - fprintf(stderr, "%s.%d: event-handler poll failed\n", - __FILE__, __LINE__); - + log_error_write(ev->srv, __FILE__, __LINE__, "S", + "event-handler poll failed"); + return NULL; } - break; + return ev; case FDEVENT_HANDLER_SELECT: if (0 != fdevent_select_init(ev)) { - fprintf(stderr, "%s.%d: event-handler select failed\n", - __FILE__, __LINE__); + log_error_write(ev->srv, __FILE__, __LINE__, "S", + "event-handler select failed"); return NULL; } - break; - case FDEVENT_HANDLER_LINUX_RTSIG: - if (0 != fdevent_linux_rtsig_init(ev)) { - fprintf(stderr, "%s.%d: event-handler linux-rtsig failed, try to set server.event-handler = \"poll\" or \"select\"\n", - __FILE__, __LINE__); - return NULL; - } - break; + return ev; case FDEVENT_HANDLER_LINUX_SYSEPOLL: if (0 != fdevent_linux_sysepoll_init(ev)) { - fprintf(stderr, "%s.%d: event-handler linux-sysepoll failed, try to set server.event-handler = \"poll\" or \"select\"\n", - __FILE__, __LINE__); + log_error_write(ev->srv, __FILE__, __LINE__, "S", + "event-handler linux-sysepoll failed, try to set server.event-handler = \"poll\" or \"select\""); return NULL; } - break; + return ev; case FDEVENT_HANDLER_SOLARIS_DEVPOLL: if (0 != fdevent_solaris_devpoll_init(ev)) { - fprintf(stderr, "%s.%d: event-handler solaris-devpoll failed, try to set server.event-handler = \"poll\" or \"select\"\n", - __FILE__, __LINE__); + log_error_write(ev->srv, __FILE__, __LINE__, "S", + "event-handler solaris-devpoll failed, try to set server.event-handler = \"poll\" or \"select\""); return NULL; } - break; + return ev; + case FDEVENT_HANDLER_SOLARIS_PORT: + if (0 != fdevent_solaris_port_init(ev)) { + log_error_write(ev->srv, __FILE__, __LINE__, "S", + "event-handler solaris-eventports failed, try to set server.event-handler = \"poll\" or \"select\""); + return NULL; + } + return ev; case FDEVENT_HANDLER_FREEBSD_KQUEUE: if (0 != fdevent_freebsd_kqueue_init(ev)) { - fprintf(stderr, "%s.%d: event-handler freebsd-kqueue failed, try to set server.event-handler = \"poll\" or \"select\"\n", - __FILE__, __LINE__); + log_error_write(ev->srv, __FILE__, __LINE__, "S", + "event-handler freebsd-kqueue failed, try to set server.event-handler = \"poll\" or \"select\""); + return NULL; + } + return ev; + case FDEVENT_HANDLER_LIBEV: + if (0 != fdevent_libev_init(ev)) { + log_error_write(ev->srv, __FILE__, __LINE__, "S", + "event-handler libev failed, try to set server.event-handler = \"poll\" or \"select\""); return NULL; } + return ev; + case FDEVENT_HANDLER_UNSET: break; - default: - fprintf(stderr, "%s.%d: event-handler is unknown, try to set server.event-handler = \"poll\" or \"select\"\n", - __FILE__, __LINE__); - return NULL; } - return ev; + log_error_write(ev->srv, __FILE__, __LINE__, "S", + "event-handler is unknown, try to set server.event-handler = \"poll\" or \"select\""); + return NULL; } void fdevent_free(fdevents *ev) { size_t i; if (!ev) return; - + if (ev->free) ev->free(ev); - + for (i = 0; i < ev->maxfds; i++) { if (ev->fdarray[i]) free(ev->fdarray[i]); } - + free(ev->fdarray); free(ev); } int fdevent_reset(fdevents *ev) { if (ev->reset) return ev->reset(ev); - + return 0; } -fdnode *fdnode_init() { +static fdnode *fdnode_init(void) { fdnode *fdn; - + fdn = calloc(1, sizeof(*fdn)); fdn->fd = -1; return fdn; } -void fdnode_free(fdnode *fdn) { +static void fdnode_free(fdnode *fdn) { free(fdn); } int fdevent_register(fdevents *ev, int fd, fdevent_handler handler, void *ctx) { fdnode *fdn; - + fdn = fdnode_init(); fdn->handler = handler; fdn->fd = fd; fdn->ctx = ctx; - + fdn->handler_ctx = NULL; + fdn->events = 0; + ev->fdarray[fd] = fdn; return 0; @@ -119,33 +129,40 @@ int fdevent_register(fdevents *ev, int fd, fdevent_handler handler, void *ctx) { int fdevent_unregister(fdevents *ev, int fd) { fdnode *fdn; - if (!ev) return 0; + + if (!ev) return 0; fdn = ev->fdarray[fd]; - + + assert(fdn->events == 0); + fdnode_free(fdn); - + ev->fdarray[fd] = NULL; - + return 0; } int fdevent_event_del(fdevents *ev, int *fde_ndx, int fd) { int fde = fde_ndx ? *fde_ndx : -1; - + + if (NULL == ev->fdarray[fd]) return 0; + if (ev->event_del) fde = ev->event_del(ev, fde, fd); - + ev->fdarray[fd]->events = 0; + if (fde_ndx) *fde_ndx = fde; - + return 0; } -int fdevent_event_add(fdevents *ev, int *fde_ndx, int fd, int events) { +int fdevent_event_set(fdevents *ev, int *fde_ndx, int fd, int events) { int fde = fde_ndx ? *fde_ndx : -1; - - if (ev->event_add) fde = ev->event_add(ev, fde, fd, events); - + + if (ev->event_set) fde = ev->event_set(ev, fde, fd, events); + ev->fdarray[fd]->events = events; + if (fde_ndx) *fde_ndx = fde; - + return 0; } @@ -156,27 +173,27 @@ int fdevent_poll(fdevents *ev, int timeout_ms) { int fdevent_event_get_revent(fdevents *ev, size_t ndx) { if (ev->event_get_revent == NULL) SEGFAULT(); - + return ev->event_get_revent(ev, ndx); } int fdevent_event_get_fd(fdevents *ev, size_t ndx) { if (ev->event_get_fd == NULL) SEGFAULT(); - + return ev->event_get_fd(ev, ndx); } fdevent_handler fdevent_get_handler(fdevents *ev, int fd) { if (ev->fdarray[fd] == NULL) SEGFAULT(); if (ev->fdarray[fd]->fd != fd) SEGFAULT(); - + return ev->fdarray[fd]->handler; } void * fdevent_get_context(fdevents *ev, int fd) { if (ev->fdarray[fd] == NULL) SEGFAULT(); if (ev->fdarray[fd]->fd != fd) SEGFAULT(); - + return ev->fdarray[fd]->ctx; } @@ -186,7 +203,7 @@ int fdevent_fcntl_set(fdevents *ev, int fd) { fcntl(fd, F_SETFD, FD_CLOEXEC); #endif if ((ev) && (ev->fcntl_set)) return ev->fcntl_set(ev, fd); -#ifdef O_NONBLOCK +#ifdef O_NONBLOCK return fcntl(fd, F_SETFL, O_NONBLOCK | O_RDWR); #else return 0; @@ -196,7 +213,7 @@ int fdevent_fcntl_set(fdevents *ev, int fd) { int fdevent_event_next_fdndx(fdevents *ev, int ndx) { if (ev->event_next_fdndx) return ev->event_next_fdndx(ev, ndx); - + return -1; } diff --git a/src/fdevent.h b/src/fdevent.h index 0bc05ca..9dd9a6c 100644 --- a/src/fdevent.h +++ b/src/fdevent.h @@ -2,34 +2,30 @@ #define _FDEVENT_H_ #ifdef HAVE_CONFIG_H -#include "config.h" +# include "config.h" #endif + #include "settings.h" #include "bitset.h" +#if defined HAVE_STDINT_H +# include <stdint.h> +#elif defined HAVE_INTTYPES_H +# include <inttypes.h> +#endif + +#include <sys/types.h> + /* select event-system */ #if defined(HAVE_EPOLL_CTL) && defined(HAVE_SYS_EPOLL_H) -# if defined HAVE_STDINT_H -# include <stdint.h> -# endif # define USE_LINUX_EPOLL -# include <sys/epoll.h> #endif -/* MacOS 10.3.x has poll.h under /usr/include/, all other unixes +/* MacOS 10.3.x has poll.h under /usr/include/, all other unixes * under /usr/include/sys/ */ #if defined HAVE_POLL && (defined(HAVE_SYS_POLL_H) || defined(HAVE_POLL_H)) # define USE_POLL -# ifdef HAVE_POLL_H -# include <poll.h> -# else -# include <sys/poll.h> -# endif -# if defined HAVE_SIGTIMEDWAIT && defined(__linux__) -# define USE_LINUX_SIGIO -# include <signal.h> -# endif #endif #if defined HAVE_SELECT @@ -44,21 +40,27 @@ #if defined HAVE_SYS_DEVPOLL_H && defined(__sun) # define USE_SOLARIS_DEVPOLL -# include <sys/devpoll.h> +#endif + +#if defined HAVE_PORT_H && defined HAVE_PORT_CREATE && defined(__sun) +# define USE_SOLARIS_PORT +# include <port.h> #endif #if defined HAVE_SYS_EVENT_H && defined HAVE_KQUEUE # define USE_FREEBSD_KQUEUE -# include <sys/event.h> #endif -#if defined HAVE_SYS_PORT_H && defined HAVE_PORT_CREATE -# define USE_SOLARIS_PORT -# include <sys/port.h> +#if defined HAVE_LIBEV +# define USE_LIBEV #endif +struct server; -typedef handler_t (*fdevent_handler)(void *srv, void *ctx, int revents); +typedef handler_t (*fdevent_handler)(struct server *srv, void *ctx, int revents); + +/* these are the POLL* values from <bits/poll.h> (linux poll) + */ #define FDEVENT_IN BV(0) #define FDEVENT_PRI BV(1) @@ -67,134 +69,115 @@ typedef handler_t (*fdevent_handler)(void *srv, void *ctx, int revents); #define FDEVENT_HUP BV(4) #define FDEVENT_NVAL BV(5) -typedef enum { FD_EVENT_TYPE_UNSET = -1, - FD_EVENT_TYPE_CONNECTION, - FD_EVENT_TYPE_FCGI_CONNECTION, - FD_EVENT_TYPE_DIRWATCH, - FD_EVENT_TYPE_CGI_CONNECTION +typedef enum { FD_EVENT_TYPE_UNSET = -1, + FD_EVENT_TYPE_CONNECTION, + FD_EVENT_TYPE_FCGI_CONNECTION, + FD_EVENT_TYPE_DIRWATCH, + FD_EVENT_TYPE_CGI_CONNECTION } fd_event_t; -typedef enum { FDEVENT_HANDLER_UNSET, +typedef enum { FDEVENT_HANDLER_UNSET, FDEVENT_HANDLER_SELECT, FDEVENT_HANDLER_POLL, - FDEVENT_HANDLER_LINUX_RTSIG, FDEVENT_HANDLER_LINUX_SYSEPOLL, FDEVENT_HANDLER_SOLARIS_DEVPOLL, + FDEVENT_HANDLER_SOLARIS_PORT, FDEVENT_HANDLER_FREEBSD_KQUEUE, - FDEVENT_HANDLER_SOLARIS_PORT + FDEVENT_HANDLER_LIBEV } fdevent_handler_t; -/** - * a mapping from fd to connection structure - * - */ -typedef struct { - int fd; /**< the fd */ - void *conn; /**< a reference the corresponding data-structure */ - fd_event_t fd_type; /**< type of the fd */ - int events; /**< registered events */ - int revents; -} fd_conn; - -typedef struct { - fd_conn *ptr; - - size_t size; - size_t used; -} fd_conn_buffer; - -/** - * array of unused fd's - * - */ typedef struct _fdnode { fdevent_handler handler; void *ctx; + void *handler_ctx; int fd; - - struct _fdnode *prev, *next; + int events; } fdnode; +/** + * array of unused fd's + * + */ + typedef struct { int *ptr; - + size_t used; size_t size; } buffer_int; /** * fd-event handler for select(), poll() and rt-signals on Linux 2.4 - * + * */ typedef struct fdevents { + struct server *srv; fdevent_handler_t type; - + fdnode **fdarray; size_t maxfds; - -#ifdef USE_LINUX_SIGIO - int in_sigio; - int signum; - sigset_t sigset; - siginfo_t siginfo; - bitset *sigbset; -#endif + #ifdef USE_LINUX_EPOLL int epoll_fd; struct epoll_event *epoll_events; #endif #ifdef USE_POLL struct pollfd *pollfds; - + size_t size; size_t used; - + buffer_int unused; #endif #ifdef USE_SELECT fd_set select_read; fd_set select_write; fd_set select_error; - + fd_set select_set_read; fd_set select_set_write; fd_set select_set_error; - + int select_max_fd; #endif #ifdef USE_SOLARIS_DEVPOLL int devpoll_fd; struct pollfd *devpollfds; #endif +#ifdef USE_SOLARIS_PORT + port_event_t *port_events; +#endif #ifdef USE_FREEBSD_KQUEUE int kq_fd; struct kevent *kq_results; - bitset *kq_bevents; #endif #ifdef USE_SOLARIS_PORT int port_fd; #endif +#ifdef USE_LIBEV + struct ev_loop *libev_loop; +#endif int (*reset)(struct fdevents *ev); void (*free)(struct fdevents *ev); - - int (*event_add)(struct fdevents *ev, int fde_ndx, int fd, int events); + + int (*event_set)(struct fdevents *ev, int fde_ndx, int fd, int events); int (*event_del)(struct fdevents *ev, int fde_ndx, int fd); int (*event_get_revent)(struct fdevents *ev, size_t ndx); int (*event_get_fd)(struct fdevents *ev, size_t ndx); - + int (*event_next_fdndx)(struct fdevents *ev, int ndx); - + int (*poll)(struct fdevents *ev, int timeout_ms); - + int (*fcntl_set)(struct fdevents *ev, int fd); } fdevents; -fdevents *fdevent_init(size_t maxfds, fdevent_handler_t type); -int fdevent_reset(fdevents *ev); +fdevents *fdevent_init(struct server *srv, size_t maxfds, fdevent_handler_t type); +int fdevent_reset(fdevents *ev); /* "init" after fork() */ void fdevent_free(fdevents *ev); -int fdevent_event_add(fdevents *ev, int *fde_ndx, int fd, int events); +int fdevent_event_set(fdevents *ev, int *fde_ndx, int fd, int events); /* events can be FDEVENT_IN, FDEVENT_OUT or FDEVENT_IN | FDEVENT_OUT */ int fdevent_event_del(fdevents *ev, int *fde_ndx, int fd); int fdevent_event_get_revent(fdevents *ev, size_t ndx); int fdevent_event_get_fd(fdevents *ev, size_t ndx); @@ -212,11 +195,10 @@ int fdevent_fcntl_set(fdevents *ev, int fd); int fdevent_select_init(fdevents *ev); int fdevent_poll_init(fdevents *ev); -int fdevent_linux_rtsig_init(fdevents *ev); int fdevent_linux_sysepoll_init(fdevents *ev); int fdevent_solaris_devpoll_init(fdevents *ev); +int fdevent_solaris_port_init(fdevents *ev); int fdevent_freebsd_kqueue_init(fdevents *ev); +int fdevent_libev_init(fdevents *ev); #endif - - diff --git a/src/fdevent_freebsd_kqueue.c b/src/fdevent_freebsd_kqueue.c index b955726..220e265 100644 --- a/src/fdevent_freebsd_kqueue.c +++ b/src/fdevent_freebsd_kqueue.c @@ -1,3 +1,7 @@ +#include "fdevent.h" +#include "buffer.h" +#include "log.h" + #include <sys/types.h> #include <unistd.h> @@ -8,81 +12,97 @@ #include <signal.h> #include <fcntl.h> -#include "fdevent.h" -#include "settings.h" -#include "buffer.h" -#include "server.h" - #ifdef USE_FREEBSD_KQUEUE -#include <sys/event.h> -#include <sys/time.h> +# include <sys/event.h> +# include <sys/time.h> static void fdevent_freebsd_kqueue_free(fdevents *ev) { close(ev->kq_fd); free(ev->kq_results); - bitset_free(ev->kq_bevents); } static int fdevent_freebsd_kqueue_event_del(fdevents *ev, int fde_ndx, int fd) { - int filter, ret; - struct kevent kev; + int ret, n = 0; + struct kevent kev[2]; struct timespec ts; + int oevents; if (fde_ndx < 0) return -1; - filter = bitset_test_bit(ev->kq_bevents, fd) ? EVFILT_READ : EVFILT_WRITE; + oevents = ev->fdarray[fd]->events; - EV_SET(&kev, fd, filter, EV_DELETE, 0, 0, NULL); + if (oevents & FDEVENT_IN) { + EV_SET(&kev[n], fd, EVFILT_READ, EV_DELETE, 0, 0, NULL); + n++; + } + if (oevents & FDEVENT_OUT) { + EV_SET(&kev[n], fd, EVFILT_WRITE, EV_DELETE, 0, 0, NULL); + n++; + } + + if (0 == n) return -1; ts.tv_sec = 0; ts.tv_nsec = 0; ret = kevent(ev->kq_fd, - &kev, 1, - NULL, 0, - &ts); + &kev, n, + NULL, 0, + &ts); if (ret == -1) { - fprintf(stderr, "%s.%d: kqueue failed polling: %s\n", - __FILE__, __LINE__, strerror(errno)); + log_error_write(ev->srv, __FILE__, __LINE__, "SS", + "kqueue event delete failed: ", strerror(errno)); return -1; } - + return -1; } -static int fdevent_freebsd_kqueue_event_add(fdevents *ev, int fde_ndx, int fd, int events) { - int filter, ret; - struct kevent kev; +static int fdevent_freebsd_kqueue_event_set(fdevents *ev, int fde_ndx, int fd, int events) { + int ret, n = 0; + struct kevent kev[2]; struct timespec ts; + int oevents = ev->fdarray[fd]->events; + int addevents = events & ~oevents; + int delevents = ~events & oevents; UNUSED(fde_ndx); - filter = (events & FDEVENT_IN) ? EVFILT_READ : EVFILT_WRITE; + if (events == oevents) return fd; - EV_SET(&kev, fd, filter, EV_ADD|EV_CLEAR, 0, 0, NULL); + if (addevents & FDEVENT_IN) { + EV_SET(&kev[n], fd, EVFILT_READ, EV_ADD|EV_CLEAR, 0, 0, NULL); + n++; + } else if (delevents & FDEVENT_IN) { + EV_SET(&kev[n], fd, EVFILT_READ, EV_DELETE, 0, 0, NULL); + n++; + } + if (addevents & FDEVENT_OUT) { + EV_SET(&kev[n], fd, EVFILT_WRITE, EV_ADD|EV_CLEAR, 0, 0, NULL); + n++; + } else if (delevents & FDEVENT_OUT) { + EV_SET(&kev[n], fd, EVFILT_WRITE, EV_DELETE, 0, 0, NULL); + n++; + } + + if (0 == n) return fd; ts.tv_sec = 0; ts.tv_nsec = 0; - + ret = kevent(ev->kq_fd, - &kev, 1, - NULL, 0, - &ts); + kev, n, + NULL, 0, + &ts); if (ret == -1) { - fprintf(stderr, "%s.%d: kqueue failed polling: %s\n", - __FILE__, __LINE__, strerror(errno)); + log_error_write(ev->srv, __FILE__, __LINE__, "SS", + "kqueue event set failed: ", strerror(errno)); return -1; } - - if (filter == EVFILT_READ) { - bitset_set_bit(ev->kq_bevents, fd); - } else { - bitset_clear_bit(ev->kq_bevents, fd); - } return fd; } @@ -95,9 +115,9 @@ static int fdevent_freebsd_kqueue_poll(fdevents *ev, int timeout_ms) { ts.tv_nsec = (timeout_ms % 1000) * 1000000; ret = kevent(ev->kq_fd, - NULL, 0, - ev->kq_results, ev->maxfds, - &ts); + NULL, 0, + ev->kq_results, ev->maxfds, + &ts); if (ret == -1) { switch(errno) { @@ -105,8 +125,8 @@ static int fdevent_freebsd_kqueue_poll(fdevents *ev, int timeout_ms) { /* we got interrupted, perhaps just a SIGCHLD of a CGI script */ return 0; default: - fprintf(stderr, "%s.%d: kqueue failed polling: %s\n", - __FILE__, __LINE__, strerror(errno)); + log_error_write(ev->srv, __FILE__, __LINE__, "SS", + "kqueue failed polling: ", strerror(errno)); break; } } @@ -124,7 +144,7 @@ static int fdevent_freebsd_kqueue_event_get_revent(fdevents *ev, size_t ndx) { } else if (e == EVFILT_WRITE) { events |= FDEVENT_OUT; } - + e = ev->kq_results[ndx].flags; if (e & EV_EOF) { @@ -150,12 +170,12 @@ static int fdevent_freebsd_kqueue_event_next_fdndx(fdevents *ev, int ndx) { static int fdevent_freebsd_kqueue_reset(fdevents *ev) { if (-1 == (ev->kq_fd = kqueue())) { - fprintf(stderr, "%s.%d: kqueue failed (%s), try to set server.event-handler = \"poll\" or \"select\"\n", - __FILE__, __LINE__, strerror(errno)); - + log_error_write(ev->srv, __FILE__, __LINE__, "SSS", + "kqueue failed (", strerror(errno), "), try to set server.event-handler = \"poll\" or \"select\""); + return -1; } - + return 0; } @@ -170,7 +190,7 @@ int fdevent_freebsd_kqueue_init(fdevents *ev) { SET(reset); SET(event_del); - SET(event_add); + SET(event_set); SET(event_next_fdndx); SET(event_get_fd); @@ -179,14 +199,13 @@ int fdevent_freebsd_kqueue_init(fdevents *ev) { ev->kq_fd = -1; ev->kq_results = calloc(ev->maxfds, sizeof(*ev->kq_results)); - ev->kq_bevents = bitset_init(ev->maxfds); /* check that kqueue works */ if (-1 == (ev->kq_fd = kqueue())) { - fprintf(stderr, "%s.%d: kqueue failed (%s), try to set server.event-handler = \"poll\" or \"select\"\n", - __FILE__, __LINE__, strerror(errno)); - + log_error_write(ev->srv, __FILE__, __LINE__, "SSS", + "kqueue failed (", strerror(errno), "), try to set server.event-handler = \"poll\" or \"select\""); + return -1; } @@ -199,8 +218,8 @@ int fdevent_freebsd_kqueue_init(fdevents *ev) { int fdevent_freebsd_kqueue_init(fdevents *ev) { UNUSED(ev); - fprintf(stderr, "%s.%d: kqueue not available, try to set server.event-handler = \"poll\" or \"select\"\n", - __FILE__, __LINE__); + log_error_write(ev->srv, __FILE__, __LINE__, "S", + "kqueue not available, try to set server.event-handler = \"poll\" or \"select\""); return -1; } diff --git a/src/fdevent_libev.c b/src/fdevent_libev.c new file mode 100644 index 0000000..de62676 --- /dev/null +++ b/src/fdevent_libev.c @@ -0,0 +1,171 @@ +#include "fdevent.h" +#include "buffer.h" +#include "log.h" + +#include <assert.h> + +#ifdef USE_LIBEV + +# include <ev.h> + +static void io_watcher_cb(struct ev_loop *loop, ev_io *w, int revents) { + fdevents *ev = w->data; + fdnode *fdn = ev->fdarray[w->fd]; + int r = 0; + UNUSED(loop); + + if (revents & EV_READ) r |= FDEVENT_IN; + if (revents & EV_WRITE) r |= FDEVENT_OUT; + if (revents & EV_ERROR) r |= FDEVENT_ERR; + + switch (r = (*fdn->handler)(ev->srv, fdn->ctx, r)) { + case HANDLER_FINISHED: + case HANDLER_GO_ON: + case HANDLER_WAIT_FOR_EVENT: + case HANDLER_WAIT_FOR_FD: + break; + case HANDLER_ERROR: + /* should never happen */ + SEGFAULT(); + break; + default: + log_error_write(ev->srv, __FILE__, __LINE__, "d", r); + break; + } +} + +static void fdevent_libev_free(fdevents *ev) { + UNUSED(ev); +} + +static int fdevent_libev_event_del(fdevents *ev, int fde_ndx, int fd) { + fdnode *fdn; + ev_io *watcher; + + if (-1 == fde_ndx) return -1; + + fdn = ev->fdarray[fd]; + watcher = fdn->handler_ctx; + + if (!watcher) return -1; + + ev_io_stop(ev->libev_loop, watcher); + free(watcher); + fdn->handler_ctx = NULL; + + return -1; +} + +static int fdevent_libev_event_set(fdevents *ev, int fde_ndx, int fd, int events) { + fdnode *fdn = ev->fdarray[fd]; + ev_io *watcher = fdn->handler_ctx; + int ev_events = 0; + UNUSED(fde_ndx); + + if (events & FDEVENT_IN) ev_events |= EV_READ; + if (events & FDEVENT_OUT) ev_events |= EV_WRITE; + + if (!watcher) { + fdn->handler_ctx = watcher = calloc(1, sizeof(ev_io)); + assert(watcher); + + ev_io_init(watcher, io_watcher_cb, fd, ev_events); + watcher->data = ev; + ev_io_start(ev->libev_loop, watcher); + } else { + if ((watcher->events & (EV_READ | EV_WRITE)) != ev_events) { + ev_io_stop(ev->libev_loop, watcher); + ev_io_set(watcher, watcher->fd, ev_events); + ev_io_start(ev->libev_loop, watcher); + } + } + + return fd; +} + +static void timeout_watcher_cb(struct ev_loop *loop, ev_timer *w, int revents) { + UNUSED(loop); + UNUSED(w); + UNUSED(revents); +} + + +static int fdevent_libev_poll(fdevents *ev, int timeout_ms) { + ev_timer timeout_watcher; + + ev_init(&timeout_watcher, timeout_watcher_cb); + ev_timer_set(&timeout_watcher, ((ev_tstamp) timeout_ms)/1000.0, 0.0); + ev_timer_start(ev->libev_loop, &timeout_watcher); + + ev_loop(ev->libev_loop, EVLOOP_ONESHOT); + + ev_timer_stop(ev->libev_loop, &timeout_watcher); + + return 0; +} + +static int fdevent_libev_event_get_revent(fdevents *ev, size_t ndx) { + UNUSED(ev); + UNUSED(ndx); + + return 0; +} + +static int fdevent_libev_event_get_fd(fdevents *ev, size_t ndx) { + UNUSED(ev); + UNUSED(ndx); + + return -1; +} + +static int fdevent_libev_event_next_fdndx(fdevents *ev, int ndx) { + UNUSED(ev); + UNUSED(ndx); + + return -1; +} + +static int fdevent_libev_reset(fdevents *ev) { + UNUSED(ev); + + ev_default_fork(); + + return 0; +} + +int fdevent_libev_init(fdevents *ev) { + ev->type = FDEVENT_HANDLER_LIBEV; +#define SET(x) \ + ev->x = fdevent_libev_##x; + + SET(free); + SET(poll); + SET(reset); + + SET(event_del); + SET(event_set); + + SET(event_next_fdndx); + SET(event_get_fd); + SET(event_get_revent); + + if (NULL == (ev->libev_loop = ev_default_loop(0))) { + log_error_write(ev->srv, __FILE__, __LINE__, "S", + "ev_default_loop failed , try to set server.event-handler = \"poll\" or \"select\""); + + return -1; + } + + return 0; +} + +#else +int fdevent_libev_init(fdevents *ev) { + UNUSED(ev); + + log_error_write(ev->srv, __FILE__, __LINE__, "S", + "libev not supported, try to set server.event-handler = \"poll\" or \"select\""); + + return -1; +} +#endif diff --git a/src/fdevent_linux_rtsig.c b/src/fdevent_linux_rtsig.c deleted file mode 100644 index dcefff8..0000000 --- a/src/fdevent_linux_rtsig.c +++ /dev/null @@ -1,260 +0,0 @@ -#include <sys/types.h> - -#include <unistd.h> -#include <stdlib.h> -#include <stdio.h> -#include <string.h> -#include <errno.h> -#include <signal.h> -#include <limits.h> - -#define __USE_GNU -#include <fcntl.h> - -#include "fdevent.h" -#include "settings.h" -#include "buffer.h" - -#ifdef USE_LINUX_SIGIO -static void fdevent_linux_rtsig_free(fdevents *ev) { - free(ev->pollfds); - if (ev->unused.ptr) free(ev->unused.ptr); - - bitset_free(ev->sigbset); -} - - -static int fdevent_linux_rtsig_event_del(fdevents *ev, int fde_ndx, int fd) { - if (fde_ndx < 0) return -1; - - if ((size_t)fde_ndx >= ev->used) { - fprintf(stderr, "%s.%d: del! out of range %d %zu\n", __FILE__, __LINE__, fde_ndx, ev->used); - SEGFAULT(); - } - - if (ev->pollfds[fde_ndx].fd == fd) { - size_t k = fde_ndx; - - ev->pollfds[k].fd = -1; - - bitset_clear_bit(ev->sigbset, fd); - - if (ev->unused.size == 0) { - ev->unused.size = 16; - ev->unused.ptr = malloc(sizeof(*(ev->unused.ptr)) * ev->unused.size); - } else if (ev->unused.size == ev->unused.used) { - ev->unused.size += 16; - ev->unused.ptr = realloc(ev->unused.ptr, sizeof(*(ev->unused.ptr)) * ev->unused.size); - } - - ev->unused.ptr[ev->unused.used++] = k; - } else { - fprintf(stderr, "%s.%d: del! %d %d\n", __FILE__, __LINE__, ev->pollfds[fde_ndx].fd, fd); - - SEGFAULT(); - } - - return -1; -} - -#if 0 -static int fdevent_linux_rtsig_event_compress(fdevents *ev) { - size_t j; - - if (ev->used == 0) return 0; - if (ev->unused.used != 0) return 0; - - for (j = ev->used - 1; j + 1 > 0; j--) { - if (ev->pollfds[j].fd == -1) ev->used--; - } - - - return 0; -} -#endif - -static int fdevent_linux_rtsig_event_add(fdevents *ev, int fde_ndx, int fd, int events) { - /* known index */ - if (fde_ndx != -1) { - if (ev->pollfds[fde_ndx].fd == fd) { - ev->pollfds[fde_ndx].events = events; - - return fde_ndx; - } - fprintf(stderr, "%s.%d: add: (%d, %d)\n", __FILE__, __LINE__, fde_ndx, ev->pollfds[fde_ndx].fd); - SEGFAULT(); - } - - if (ev->unused.used > 0) { - int k = ev->unused.ptr[--ev->unused.used]; - - ev->pollfds[k].fd = fd; - ev->pollfds[k].events = events; - - bitset_set_bit(ev->sigbset, fd); - - return k; - } else { - if (ev->size == 0) { - ev->size = 16; - ev->pollfds = malloc(sizeof(*ev->pollfds) * ev->size); - } else if (ev->size == ev->used) { - ev->size += 16; - ev->pollfds = realloc(ev->pollfds, sizeof(*ev->pollfds) * ev->size); - } - - ev->pollfds[ev->used].fd = fd; - ev->pollfds[ev->used].events = events; - - bitset_set_bit(ev->sigbset, fd); - - return ev->used++; - } -} - -static int fdevent_linux_rtsig_poll(fdevents *ev, int timeout_ms) { - struct timespec ts; - int r; - -#if 0 - fdevent_linux_rtsig_event_compress(ev); -#endif - - ev->in_sigio = 1; - - ts.tv_sec = timeout_ms / 1000; - ts.tv_nsec = (timeout_ms % 1000) * 1000000; - r = sigtimedwait(&(ev->sigset), &(ev->siginfo), &(ts)); - - if (r == -1) { - if (errno == EAGAIN) return 0; - return r; - } else if (r == SIGIO) { - struct sigaction act; - - /* flush the signal queue */ - memset(&act, 0, sizeof(act)); - act.sa_handler = SIG_IGN; - sigaction(ev->signum, &act, NULL); - - /* re-enable the signal queue */ - act.sa_handler = SIG_DFL; - sigaction(ev->signum, &act, NULL); - - ev->in_sigio = 0; - r = poll(ev->pollfds, ev->used, timeout_ms); - - return r; - } else if (r == ev->signum) { -# if 0 - fprintf(stderr, "event: %d %02lx\n", ev->siginfo.si_fd, ev->siginfo.si_band); -# endif - return bitset_test_bit(ev->sigbset, ev->siginfo.si_fd); - } else { - /* ? */ - return -1; - } -} - -static int fdevent_linux_rtsig_event_get_revent(fdevents *ev, size_t ndx) { - if (ev->in_sigio == 1) { -# if 0 - if (ev->siginfo.si_band == POLLERR) { - fprintf(stderr, "event: %d %02lx %02x %s\n", ev->siginfo.si_fd, ev->siginfo.si_band, errno, strerror(errno)); - } -# endif - if (ndx != 0) { - fprintf(stderr, "+\n"); - return 0; - } - - return ev->siginfo.si_band & 0x3f; - } else { - if (ndx >= ev->used) { - fprintf(stderr, "%s.%d: event: %zu %zu\n", __FILE__, __LINE__, ndx, ev->used); - return 0; - } - return ev->pollfds[ndx].revents; - } -} - -static int fdevent_linux_rtsig_event_get_fd(fdevents *ev, size_t ndx) { - if (ev->in_sigio == 1) { - return ev->siginfo.si_fd; - } else { - return ev->pollfds[ndx].fd; - } -} - -static int fdevent_linux_rtsig_fcntl_set(fdevents *ev, int fd) { - static pid_t pid = 0; - - if (pid == 0) pid = getpid(); - - if (-1 == fcntl(fd, F_SETSIG, ev->signum)) return -1; - - if (-1 == fcntl(fd, F_SETOWN, (int) pid)) return -1; - - return fcntl(fd, F_SETFL, O_ASYNC | O_NONBLOCK | O_RDWR); -} - - -static int fdevent_linux_rtsig_event_next_fdndx(fdevents *ev, int ndx) { - if (ev->in_sigio == 1) { - if (ndx < 0) return 0; - return -1; - } else { - size_t i; - - i = (ndx < 0) ? 0 : ndx + 1; - for (; i < ev->used; i++) { - if (ev->pollfds[i].revents) break; - } - - return i; - } -} - -int fdevent_linux_rtsig_init(fdevents *ev) { - ev->type = FDEVENT_HANDLER_LINUX_RTSIG; -#define SET(x) \ - ev->x = fdevent_linux_rtsig_##x; - - SET(free); - SET(poll); - - SET(event_del); - SET(event_add); - - SET(event_next_fdndx); - SET(fcntl_set); - SET(event_get_fd); - SET(event_get_revent); - - ev->signum = SIGRTMIN + 1; - - sigemptyset(&(ev->sigset)); - sigaddset(&(ev->sigset), ev->signum); - sigaddset(&(ev->sigset), SIGIO); - if (-1 == sigprocmask(SIG_BLOCK, &(ev->sigset), NULL)) { - fprintf(stderr, "%s.%d: sigprocmask failed (%s), try to set server.event-handler = \"poll\" or \"select\"\n", - __FILE__, __LINE__, strerror(errno)); - - return -1; - } - - ev->in_sigio = 1; - - ev->sigbset = bitset_init(ev->maxfds); - - return 0; -} -#else -int fdevent_linux_rtsig_init(fdevents *ev) { - UNUSED(ev); - - fprintf(stderr, "%s.%d: linux-rtsig not supported, try to set server.event-handler = \"poll\" or \"select\"\n", - __FILE__, __LINE__); - return -1; -} -#endif diff --git a/src/fdevent_linux_sysepoll.c b/src/fdevent_linux_sysepoll.c index 31caabd..f761ed6 100644 --- a/src/fdevent_linux_sysepoll.c +++ b/src/fdevent_linux_sysepoll.c @@ -1,3 +1,7 @@ +#include "fdevent.h" +#include "buffer.h" +#include "log.h" + #include <sys/types.h> #include <unistd.h> @@ -8,11 +12,10 @@ #include <signal.h> #include <fcntl.h> -#include "fdevent.h" -#include "settings.h" -#include "buffer.h" - #ifdef USE_LINUX_EPOLL + +# include <sys/epoll.h> + static void fdevent_linux_sysepoll_free(fdevents *ev) { close(ev->epoll_fd); free(ev->epoll_events); @@ -20,36 +23,37 @@ static void fdevent_linux_sysepoll_free(fdevents *ev) { static int fdevent_linux_sysepoll_event_del(fdevents *ev, int fde_ndx, int fd) { struct epoll_event ep; - + if (fde_ndx < 0) return -1; - + memset(&ep, 0, sizeof(ep)); - + ep.data.fd = fd; ep.data.ptr = NULL; - + if (0 != epoll_ctl(ev->epoll_fd, EPOLL_CTL_DEL, fd, &ep)) { - fprintf(stderr, "%s.%d: epoll_ctl failed: %s, dying\n", __FILE__, __LINE__, strerror(errno)); - + log_error_write(ev->srv, __FILE__, __LINE__, "SSS", + "epoll_ctl failed: ", strerror(errno), ", dying"); + SEGFAULT(); - + return 0; } - - + + return -1; } -static int fdevent_linux_sysepoll_event_add(fdevents *ev, int fde_ndx, int fd, int events) { +static int fdevent_linux_sysepoll_event_set(fdevents *ev, int fde_ndx, int fd, int events) { struct epoll_event ep; int add = 0; - + if (fde_ndx == -1) add = 1; - + memset(&ep, 0, sizeof(ep)); - + ep.events = 0; - + if (events & FDEVENT_IN) ep.events |= EPOLLIN; if (events & FDEVENT_OUT) ep.events |= EPOLLOUT; @@ -60,20 +64,21 @@ static int fdevent_linux_sysepoll_event_add(fdevents *ev, int fde_ndx, int fd, i * sent. * */ - + ep.events |= EPOLLERR | EPOLLHUP /* | EPOLLET */; - + ep.data.ptr = NULL; ep.data.fd = fd; - + if (0 != epoll_ctl(ev->epoll_fd, add ? EPOLL_CTL_ADD : EPOLL_CTL_MOD, fd, &ep)) { - fprintf(stderr, "%s.%d: epoll_ctl failed: %s, dying\n", __FILE__, __LINE__, strerror(errno)); - + log_error_write(ev->srv, __FILE__, __LINE__, "SSS", + "epoll_ctl failed: ", strerror(errno), ", dying"); + SEGFAULT(); - + return 0; } - + return fd; } @@ -83,32 +88,33 @@ static int fdevent_linux_sysepoll_poll(fdevents *ev, int timeout_ms) { static int fdevent_linux_sysepoll_event_get_revent(fdevents *ev, size_t ndx) { int events = 0, e; - + e = ev->epoll_events[ndx].events; if (e & EPOLLIN) events |= FDEVENT_IN; if (e & EPOLLOUT) events |= FDEVENT_OUT; if (e & EPOLLERR) events |= FDEVENT_ERR; if (e & EPOLLHUP) events |= FDEVENT_HUP; if (e & EPOLLPRI) events |= FDEVENT_PRI; - - return e; + + return events; } static int fdevent_linux_sysepoll_event_get_fd(fdevents *ev, size_t ndx) { # if 0 - fprintf(stderr, "%s.%d: %d, %d\n", __FILE__, __LINE__, ndx, ev->epoll_events[ndx].data.fd); + log_error_write(ev->srv, __FILE__, __LINE__, "SD, D", + "fdevent_linux_sysepoll_event_get_fd: ", (int) ndx, ev->epoll_events[ndx].data.fd); # endif - + return ev->epoll_events[ndx].data.fd; } static int fdevent_linux_sysepoll_event_next_fdndx(fdevents *ev, int ndx) { size_t i; - + UNUSED(ev); i = (ndx < 0) ? 0 : ndx + 1; - + return i; } @@ -116,27 +122,27 @@ int fdevent_linux_sysepoll_init(fdevents *ev) { ev->type = FDEVENT_HANDLER_LINUX_SYSEPOLL; #define SET(x) \ ev->x = fdevent_linux_sysepoll_##x; - + SET(free); SET(poll); - + SET(event_del); - SET(event_add); - + SET(event_set); + SET(event_next_fdndx); SET(event_get_fd); SET(event_get_revent); - + if (-1 == (ev->epoll_fd = epoll_create(ev->maxfds))) { - fprintf(stderr, "%s.%d: epoll_create failed (%s), try to set server.event-handler = \"poll\" or \"select\"\n", - __FILE__, __LINE__, strerror(errno)); + log_error_write(ev->srv, __FILE__, __LINE__, "SSS", + "epoll_create failed (", strerror(errno), "), try to set server.event-handler = \"poll\" or \"select\""); return -1; } if (-1 == fcntl(ev->epoll_fd, F_SETFD, FD_CLOEXEC)) { - fprintf(stderr, "%s.%d: epoll_create failed (%s), try to set server.event-handler = \"poll\" or \"select\"\n", - __FILE__, __LINE__, strerror(errno)); + log_error_write(ev->srv, __FILE__, __LINE__, "SSS", + "fcntl on epoll-fd failed (", strerror(errno), "), try to set server.event-handler = \"poll\" or \"select\""); close(ev->epoll_fd); @@ -152,9 +158,9 @@ int fdevent_linux_sysepoll_init(fdevents *ev) { int fdevent_linux_sysepoll_init(fdevents *ev) { UNUSED(ev); - fprintf(stderr, "%s.%d: linux-sysepoll not supported, try to set server.event-handler = \"poll\" or \"select\"\n", - __FILE__, __LINE__); - + log_error_write(ev->srv, __FILE__, __LINE__, "S", + "linux-sysepoll not supported, try to set server.event-handler = \"poll\" or \"select\""); + return -1; } #endif diff --git a/src/fdevent_poll.c b/src/fdevent_poll.c index 7d8017a..e8dc385 100644 --- a/src/fdevent_poll.c +++ b/src/fdevent_poll.c @@ -1,3 +1,7 @@ +#include "fdevent.h" +#include "buffer.h" +#include "log.h" + #include <sys/types.h> #include <unistd.h> @@ -8,11 +12,14 @@ #include <signal.h> #include <fcntl.h> -#include "fdevent.h" -#include "settings.h" -#include "buffer.h" - #ifdef USE_POLL + +# ifdef HAVE_POLL_H +# include <poll.h> +# else +# include <sys/poll.h> +# endif + static void fdevent_poll_free(fdevents *ev) { free(ev->pollfds); if (ev->unused.ptr) free(ev->unused.ptr); @@ -20,19 +27,20 @@ static void fdevent_poll_free(fdevents *ev) { static int fdevent_poll_event_del(fdevents *ev, int fde_ndx, int fd) { if (fde_ndx < 0) return -1; - + if ((size_t)fde_ndx >= ev->used) { - fprintf(stderr, "%s.%d: del! out of range %d %zd\n", __FILE__, __LINE__, fde_ndx, ev->used); + log_error_write(ev->srv, __FILE__, __LINE__, "SdD", + "del! out of range ", fde_ndx, (int) ev->used); SEGFAULT(); } - + if (ev->pollfds[fde_ndx].fd == fd) { size_t k = fde_ndx; - + ev->pollfds[k].fd = -1; /* ev->pollfds[k].events = 0; */ /* ev->pollfds[k].revents = 0; */ - + if (ev->unused.size == 0) { ev->unused.size = 16; ev->unused.ptr = malloc(sizeof(*(ev->unused.ptr)) * ev->unused.size); @@ -40,47 +48,55 @@ static int fdevent_poll_event_del(fdevents *ev, int fde_ndx, int fd) { ev->unused.size += 16; ev->unused.ptr = realloc(ev->unused.ptr, sizeof(*(ev->unused.ptr)) * ev->unused.size); } - + ev->unused.ptr[ev->unused.used++] = k; } else { + log_error_write(ev->srv, __FILE__, __LINE__, "SdD", + "del! ", ev->pollfds[fde_ndx].fd, fd); + SEGFAULT(); } - + return -1; } #if 0 static int fdevent_poll_event_compress(fdevents *ev) { size_t j; - + if (ev->used == 0) return 0; if (ev->unused.used != 0) return 0; - + for (j = ev->used - 1; j + 1 > 0 && ev->pollfds[j].fd == -1; j--) ev->used--; - + return 0; } #endif -static int fdevent_poll_event_add(fdevents *ev, int fde_ndx, int fd, int events) { +static int fdevent_poll_event_set(fdevents *ev, int fde_ndx, int fd, int events) { + int pevents = 0; + if (events & FDEVENT_IN) pevents |= POLLIN; + if (events & FDEVENT_OUT) pevents |= POLLOUT; + /* known index */ - + if (fde_ndx != -1) { if (ev->pollfds[fde_ndx].fd == fd) { - ev->pollfds[fde_ndx].events = events; - + ev->pollfds[fde_ndx].events = pevents; + return fde_ndx; } - fprintf(stderr, "%s.%d: add: (%d, %d)\n", __FILE__, __LINE__, fde_ndx, ev->pollfds[fde_ndx].fd); + log_error_write(ev->srv, __FILE__, __LINE__, "SdD", + "set: ", fde_ndx, ev->pollfds[fde_ndx].fd); SEGFAULT(); } - + if (ev->unused.used > 0) { int k = ev->unused.ptr[--ev->unused.used]; - + ev->pollfds[k].fd = fd; - ev->pollfds[k].events = events; - + ev->pollfds[k].events = pevents; + return k; } else { if (ev->size == 0) { @@ -90,10 +106,10 @@ static int fdevent_poll_event_add(fdevents *ev, int fde_ndx, int fd, int events) ev->size += 16; ev->pollfds = realloc(ev->pollfds, sizeof(*ev->pollfds) * ev->size); } - + ev->pollfds[ev->used].fd = fd; - ev->pollfds[ev->used].events = events; - + ev->pollfds[ev->used].events = pevents; + return ev->used++; } } @@ -107,14 +123,16 @@ static int fdevent_poll_poll(fdevents *ev, int timeout_ms) { static int fdevent_poll_event_get_revent(fdevents *ev, size_t ndx) { int r, poll_r; + if (ndx >= ev->used) { - fprintf(stderr, "%s.%d: dying because: event: %zd >= %zd\n", __FILE__, __LINE__, ndx, ev->used); - + log_error_write(ev->srv, __FILE__, __LINE__, "sii", + "dying because: event: ", (int) ndx, (int) ev->used); + SEGFAULT(); - + return 0; } - + if (ev->pollfds[ndx].revents & POLLNVAL) { /* should never happen */ SEGFAULT(); @@ -123,7 +141,7 @@ static int fdevent_poll_event_get_revent(fdevents *ev, size_t ndx) { r = 0; poll_r = ev->pollfds[ndx].revents; - /* map POLL* to FDEVEN_* */ + /* map POLL* to FDEVEN_*; they are probably the same, but still. */ if (poll_r & POLLIN) r |= FDEVENT_IN; if (poll_r & POLLOUT) r |= FDEVENT_OUT; @@ -131,8 +149,8 @@ static int fdevent_poll_event_get_revent(fdevents *ev, size_t ndx) { if (poll_r & POLLHUP) r |= FDEVENT_HUP; if (poll_r & POLLNVAL) r |= FDEVENT_NVAL; if (poll_r & POLLPRI) r |= FDEVENT_PRI; - - return ev->pollfds[ndx].revents; + + return r; } static int fdevent_poll_event_get_fd(fdevents *ev, size_t ndx) { @@ -141,30 +159,30 @@ static int fdevent_poll_event_get_fd(fdevents *ev, size_t ndx) { static int fdevent_poll_event_next_fdndx(fdevents *ev, int ndx) { size_t i; - + i = (ndx < 0) ? 0 : ndx + 1; for (; i < ev->used; i++) { - if (ev->pollfds[i].revents) break; + if (ev->pollfds[i].revents) return i; } - - return i; + + return -1; } int fdevent_poll_init(fdevents *ev) { ev->type = FDEVENT_HANDLER_POLL; #define SET(x) \ ev->x = fdevent_poll_##x; - + SET(free); SET(poll); - + SET(event_del); - SET(event_add); - + SET(event_set); + SET(event_next_fdndx); SET(event_get_fd); SET(event_get_revent); - + return 0; } @@ -175,8 +193,9 @@ int fdevent_poll_init(fdevents *ev) { int fdevent_poll_init(fdevents *ev) { UNUSED(ev); - fprintf(stderr, "%s.%d: poll is not supported, try to set server.event-handler = \"select\"\n", - __FILE__, __LINE__); + log_error_write(srv, __FILE__, __LINE__, + "s", "poll is not supported, try to set server.event-handler = \"select\""); + return -1; } #endif diff --git a/src/fdevent_select.c b/src/fdevent_select.c index 3eb10f3..2e23dce 100644 --- a/src/fdevent_select.c +++ b/src/fdevent_select.c @@ -1,3 +1,7 @@ +#include "fdevent.h" +#include "buffer.h" +#include "log.h" + #include <sys/time.h> #include <sys/types.h> @@ -9,10 +13,6 @@ #include <fcntl.h> #include <assert.h> -#include "fdevent.h" -#include "settings.h" -#include "buffer.h" - #ifdef USE_SELECT static int fdevent_select_reset(fdevents *ev) { @@ -34,43 +34,45 @@ static int fdevent_select_event_del(fdevents *ev, int fde_ndx, int fd) { return -1; } -static int fdevent_select_event_add(fdevents *ev, int fde_ndx, int fd, int events) { +static int fdevent_select_event_set(fdevents *ev, int fde_ndx, int fd, int events) { UNUSED(fde_ndx); /* we should be protected by max-fds, but you never know */ - assert(fd < FD_SETSIZE); + assert(fd < ((int)FD_SETSIZE)); if (events & FDEVENT_IN) { FD_SET(fd, &(ev->select_set_read)); - FD_CLR(fd, &(ev->select_set_write)); + } else { + FD_CLR(fd, &(ev->select_set_read)); } if (events & FDEVENT_OUT) { - FD_CLR(fd, &(ev->select_set_read)); FD_SET(fd, &(ev->select_set_write)); + } else { + FD_CLR(fd, &(ev->select_set_write)); } FD_SET(fd, &(ev->select_set_error)); - + if (fd > ev->select_max_fd) ev->select_max_fd = fd; - + return fd; } static int fdevent_select_poll(fdevents *ev, int timeout_ms) { struct timeval tv; - + tv.tv_sec = timeout_ms / 1000; tv.tv_usec = (timeout_ms % 1000) * 1000; - + ev->select_read = ev->select_set_read; ev->select_write = ev->select_set_write; ev->select_error = ev->select_set_error; - + return select(ev->select_max_fd + 1, &(ev->select_read), &(ev->select_write), &(ev->select_error), &tv); } static int fdevent_select_event_get_revent(fdevents *ev, size_t ndx) { int revents = 0; - + if (FD_ISSET(ndx, &(ev->select_read))) { revents |= FDEVENT_IN; } @@ -80,7 +82,7 @@ static int fdevent_select_event_get_revent(fdevents *ev, size_t ndx) { if (FD_ISSET(ndx, &(ev->select_error))) { revents |= FDEVENT_ERR; } - + return revents; } @@ -92,33 +94,33 @@ static int fdevent_select_event_get_fd(fdevents *ev, size_t ndx) { static int fdevent_select_event_next_fdndx(fdevents *ev, int ndx) { int i; - + i = (ndx < 0) ? 0 : ndx + 1; - + for (; i < ev->select_max_fd + 1; i++) { - if (FD_ISSET(i, &(ev->select_read))) break; - if (FD_ISSET(i, &(ev->select_write))) break; - if (FD_ISSET(i, &(ev->select_error))) break; + if (FD_ISSET(i, &(ev->select_read))) return i; + if (FD_ISSET(i, &(ev->select_write))) return i; + if (FD_ISSET(i, &(ev->select_error))) return i; } - - return i; + + return -1; } int fdevent_select_init(fdevents *ev) { ev->type = FDEVENT_HANDLER_SELECT; #define SET(x) \ ev->x = fdevent_select_##x; - + SET(reset); SET(poll); - + SET(event_del); - SET(event_add); - + SET(event_set); + SET(event_next_fdndx); SET(event_get_fd); SET(event_get_revent); - + return 0; } diff --git a/src/fdevent_solaris_devpoll.c b/src/fdevent_solaris_devpoll.c index 91238b0..dd273e6 100644 --- a/src/fdevent_solaris_devpoll.c +++ b/src/fdevent_solaris_devpoll.c @@ -1,3 +1,7 @@ +#include "fdevent.h" +#include "buffer.h" +#include "log.h" + #include <sys/types.h> #include <unistd.h> @@ -8,12 +12,10 @@ #include <signal.h> #include <fcntl.h> -#include "fdevent.h" -#include "settings.h" -#include "buffer.h" - #ifdef USE_SOLARIS_DEVPOLL +# include <sys/devpoll.h> + static void fdevent_solaris_devpoll_free(fdevents *ev) { free(ev->devpollfds); close(ev->devpoll_fd); @@ -23,60 +25,76 @@ static void fdevent_solaris_devpoll_free(fdevents *ev) { static int fdevent_solaris_devpoll_event_del(fdevents *ev, int fde_ndx, int fd) { struct pollfd pfd; - + if (fde_ndx < 0) return -1; - + pfd.fd = fd; pfd.events = POLLREMOVE; pfd.revents = 0; - + if (-1 == write(ev->devpoll_fd, &pfd, sizeof(pfd))) { - fprintf(stderr, "%s.%d: (del) write failed: (%d, %s)\n", - __FILE__, __LINE__, - fd, strerror(errno)); - + log_error_write(ev->srv, __FILE__, __LINE__, "S(D, S)", + "(del) write failed: ", fd, strerror(errno)); + return -1; } - + return -1; } -static int fdevent_solaris_devpoll_event_add(fdevents *ev, int fde_ndx, int fd, int events) { +static int fdevent_solaris_devpoll_event_set(fdevents *ev, int fde_ndx, int fd, int events) { struct pollfd pfd; int add = 0; - + + int pevents = 0; + if (events & FDEVENT_IN) pevents |= POLLIN; + if (events & FDEVENT_OUT) pevents |= POLLOUT; + if (fde_ndx == -1) add = 1; - + pfd.fd = fd; - pfd.events = events; + pfd.events = pevents; pfd.revents = 0; - + if (-1 == write(ev->devpoll_fd, &pfd, sizeof(pfd))) { - fprintf(stderr, "%s.%d: (del) write failed: (%d, %s)\n", - __FILE__, __LINE__, - fd, strerror(errno)); - + log_error_write(ev->srv, __FILE__, __LINE__, "S(D, S)", + "(set) write failed: ", fd, strerror(errno)); + return -1; } - + return fd; } static int fdevent_solaris_devpoll_poll(fdevents *ev, int timeout_ms) { struct dvpoll dopoll; int ret; - + dopoll.dp_timeout = timeout_ms; - dopoll.dp_nfds = ev->maxfds; + dopoll.dp_nfds = ev->maxfds - 1; dopoll.dp_fds = ev->devpollfds; - + ret = ioctl(ev->devpoll_fd, DP_POLL, &dopoll); - + return ret; } static int fdevent_solaris_devpoll_event_get_revent(fdevents *ev, size_t ndx) { - return ev->devpollfds[ndx].revents; + int r, poll_r; + + r = 0; + poll_r = ev->devpollfds[ndx].revents; + + /* map POLL* to FDEVEN_*; they are probably the same, but still. */ + + if (poll_r & POLLIN) r |= FDEVENT_IN; + if (poll_r & POLLOUT) r |= FDEVENT_OUT; + if (poll_r & POLLERR) r |= FDEVENT_ERR; + if (poll_r & POLLHUP) r |= FDEVENT_HUP; + if (poll_r & POLLNVAL) r |= FDEVENT_NVAL; + if (poll_r & POLLPRI) r |= FDEVENT_PRI; + + return r; } static int fdevent_solaris_devpoll_event_get_fd(fdevents *ev, size_t ndx) { @@ -85,11 +103,11 @@ static int fdevent_solaris_devpoll_event_get_fd(fdevents *ev, size_t ndx) { static int fdevent_solaris_devpoll_event_next_fdndx(fdevents *ev, int last_ndx) { size_t i; - + UNUSED(ev); i = (last_ndx < 0) ? 0 : last_ndx + 1; - + return i; } @@ -97,15 +115,15 @@ int fdevent_solaris_devpoll_reset(fdevents *ev) { /* a forked process does only inherit the filedescriptor, * but every operation on the device will lead to a EACCES */ if ((ev->devpoll_fd = open("/dev/poll", O_RDWR)) < 0) { - fprintf(stderr, "%s.%d: opening /dev/poll failed (%s), try to set server.event-handler = \"poll\" or \"select\"\n", - __FILE__, __LINE__, strerror(errno)); + log_error_write(ev->srv, __FILE__, __LINE__, "SSS", + "opening /dev/poll failed (", strerror(errno), "), try to set server.event-handler = \"poll\" or \"select\""); return -1; } if (fcntl(ev->devpoll_fd, F_SETFD, FD_CLOEXEC) < 0) { - fprintf(stderr, "%s.%d: opening /dev/poll failed (%s), try to set server.event-handler = \"poll\" or \"select\"\n", - __FILE__, __LINE__, strerror(errno)); + log_error_write(ev->srv, __FILE__, __LINE__, "SSS", + "fcntl /dev/poll fd failed (", strerror(errno), "), try to set server.event-handler = \"poll\" or \"select\""); close(ev->devpoll_fd); @@ -117,23 +135,23 @@ int fdevent_solaris_devpoll_init(fdevents *ev) { ev->type = FDEVENT_HANDLER_SOLARIS_DEVPOLL; #define SET(x) \ ev->x = fdevent_solaris_devpoll_##x; - + SET(free); SET(poll); SET(reset); - + SET(event_del); - SET(event_add); - + SET(event_set); + SET(event_next_fdndx); SET(event_get_fd); SET(event_get_revent); - + ev->devpollfds = malloc(sizeof(*ev->devpollfds) * ev->maxfds); - + if ((ev->devpoll_fd = open("/dev/poll", O_RDWR)) < 0) { - fprintf(stderr, "%s.%d: opening /dev/poll failed (%s), try to set server.event-handler = \"poll\" or \"select\"\n", - __FILE__, __LINE__, strerror(errno)); + log_error_write(ev->srv, __FILE__, __LINE__, "SSS", + "opening /dev/poll failed (", strerror(errno), "), try to set server.event-handler = \"poll\" or \"select\""); return -1; } @@ -150,9 +168,9 @@ int fdevent_solaris_devpoll_init(fdevents *ev) { int fdevent_solaris_devpoll_init(fdevents *ev) { UNUSED(ev); - fprintf(stderr, "%s.%d: solaris-devpoll not supported, try to set server.event-handler = \"poll\" or \"select\"\n", - __FILE__, __LINE__); - + log_error_write(ev->srv, __FILE__, __LINE__, "S", + "solaris-devpoll not supported, try to set server.event-handler = \"poll\" or \"select\""); + return -1; } #endif diff --git a/src/fdevent_solaris_port.c b/src/fdevent_solaris_port.c new file mode 100644 index 0000000..8dea4d2 --- /dev/null +++ b/src/fdevent_solaris_port.c @@ -0,0 +1,172 @@ +#include "fdevent.h" +#include "buffer.h" +#include "log.h" + +#include <sys/types.h> + +#include <unistd.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include <signal.h> +#include <fcntl.h> + +#ifdef USE_SOLARIS_PORT + +static const int SOLARIS_PORT_POLL_READ = POLLIN; +static const int SOLARIS_PORT_POLL_WRITE = POLLOUT; +static const int SOLARIS_PORT_POLL_READ_WRITE = POLLIN & POLLOUT; + +static int fdevent_solaris_port_event_del(fdevents *ev, int fde_ndx, int fd) { + if (fde_ndx < 0) return -1; + + if (0 != port_dissociate(ev->port_fd, PORT_SOURCE_FD, fd)) { + log_error_write(ev->srv, __FILE__, __LINE__, "SSS", + "port_dissociate failed: ", strerror(errno), ", dying"); + + SEGFAULT(); + + return 0; + } + + return -1; +} + +static int fdevent_solaris_port_event_set(fdevents *ev, int fde_ndx, int fd, int events) { + const int* user_data = NULL; + + if ((events & FDEVENT_IN) && (events & FDEVENT_OUT)) { + user_data = &SOLARIS_PORT_POLL_READ_WRITE; + } else if (events & FDEVENT_IN) { + user_data = &SOLARIS_PORT_POLL_READ; + } else if (events & FDEVENT_OUT) { + user_data = &SOLARIS_PORT_POLL_WRITE; + } + + if (0 != port_associate(ev->port_fd, PORT_SOURCE_FD, fd, *user_data, (void*) user_data)) { + log_error_write(ev->srv, __FILE__, __LINE__, "SSS", + "port_associate failed: ", strerror(errno), ", dying"); + + SEGFAULT(); + + return 0; + } + + return fd; +} + +static int fdevent_solaris_port_event_get_revent(fdevents *ev, size_t ndx) { + int events = 0, e; + + e = ev->port_events[ndx].portev_events; + if (e & POLLIN) events |= FDEVENT_IN; + if (e & POLLOUT) events |= FDEVENT_OUT; + if (e & POLLERR) events |= FDEVENT_ERR; + if (e & POLLHUP) events |= FDEVENT_HUP; + if (e & POLLPRI) events |= FDEVENT_PRI; + if (e & POLLNVAL) events |= FDEVENT_NVAL; + + return e; +} + +static int fdevent_solaris_port_event_get_fd(fdevents *ev, size_t ndx) { + return ev->port_events[ndx].portev_object; +} + +static int fdevent_solaris_port_event_next_fdndx(fdevents *ev, int ndx) { + size_t i; + + UNUSED(ev); + + i = (ndx < 0) ? 0 : ndx + 1; + + return i; +} + +static void fdevent_solaris_port_free(fdevents *ev) { + close(ev->port_fd); + free(ev->port_events); +} + +/* if there is any error it will return the return values of port_getn, otherwise it will return number of events **/ +static int fdevent_solaris_port_poll(fdevents *ev, int timeout_ms) { + int i = 0; + int ret; + unsigned int available_events, wait_for_events = 0; + const int *user_data; + + struct timespec timeout; + + timeout.tv_sec = timeout_ms/1000L; + timeout.tv_nsec = (timeout_ms % 1000L) * 1000000L; + + /* get the number of file descriptors with events */ + if ((ret = port_getn(ev->port_fd, ev->port_events, 0, &wait_for_events, &timeout)) < 0) return ret; + + /* wait for at least one event */ + if (0 == wait_for_events) wait_for_events = 1; + + available_events = wait_for_events; + + /* get the events of the file descriptors */ + if ((ret = port_getn(ev->port_fd, ev->port_events, ev->maxfds, &available_events, &timeout)) < 0) { + /* if errno == ETIME and available_event == wait_for_events we didn't get any events */ + /* for other errors we didn't get any events either */ + if (!(errno == ETIME && wait_for_events != available_events)) return ret; + } + + for (i = 0; i < available_events; ++i) { + user_data = (const int *) ev->port_events[i].portev_user; + + if ((ret = port_associate(ev->port_fd, PORT_SOURCE_FD, ev->port_events[i].portev_object, + *user_data, (void*) user_data)) < 0) { + log_error_write(ev->srv, __FILE__, __LINE__, "SSS", + "port_associate failed: ", strerror(errno), ", dying"); + + SEGFAULT(); + + return 0; + } + } + + return available_events; +} + +int fdevent_solaris_port_init(fdevents *ev) { + ev->type = FDEVENT_HANDLER_SOLARIS_PORT; +#define SET(x) \ + ev->x = fdevent_solaris_port_##x; + + SET(free); + SET(poll); + + SET(event_del); + SET(event_set); + + SET(event_next_fdndx); + SET(event_get_fd); + SET(event_get_revent); + + if ((ev->port_fd = port_create()) < 0) { + log_error_write(ev->srv, __FILE__, __LINE__, "SSS", + "port_create() failed (", strerror(errno), "), try to set server.event-handler = \"poll\" or \"select\""); + + return -1; + } + + ev->port_events = malloc(ev->maxfds * sizeof(*ev->port_events)); + + return 0; +} + +#else +int fdevent_solaris_port_init(fdevents *ev) { + UNUSED(ev); + + log_error_write(ev->srv, __FILE__, __LINE__, "S", + "solaris-eventports not supported, try to set server.event-handler = \"poll\" or \"select\""); + + return -1; +} +#endif diff --git a/src/http-header-glue.c b/src/http-header-glue.c index 1e74098..a33ed3b 100644 --- a/src/http-header-glue.c +++ b/src/http-header-glue.c @@ -1,14 +1,14 @@ -#define _GNU_SOURCE - -#include <string.h> -#include <errno.h> -#include <time.h> - #include "base.h" #include "array.h" #include "buffer.h" #include "log.h" #include "etag.h" +#include "response.h" + +#include <string.h> +#include <errno.h> + +#include <time.h> /* * This was 'borrowed' from tcpdump. @@ -45,20 +45,20 @@ # ifdef HAVE_STRUCT_SOCKADDR_STORAGE static size_t get_sa_len(const struct sockaddr *addr) { switch (addr->sa_family) { - + # ifdef AF_INET case AF_INET: return (sizeof (struct sockaddr_in)); # endif - + # ifdef AF_INET6 case AF_INET6: return (sizeof (struct sockaddr_in6)); # endif - + default: return (sizeof (struct sockaddr)); - + } } # define SA_LEN(addr) (get_sa_len(addr)) @@ -74,7 +74,7 @@ static size_t get_sa_len(const struct sockaddr *addr) { int response_header_insert(server *srv, connection *con, const char *key, size_t keylen, const char *value, size_t vallen) { data_string *ds; - + UNUSED(srv); if (NULL == (ds = (data_string *)array_get_unused_element(con->response.headers, TYPE_STRING))) { @@ -82,36 +82,51 @@ int response_header_insert(server *srv, connection *con, const char *key, size_t } buffer_copy_string_len(ds->key, key, keylen); buffer_copy_string_len(ds->value, value, vallen); - + array_insert_unique(con->response.headers, (data_unset *)ds); - + return 0; } int response_header_overwrite(server *srv, connection *con, const char *key, size_t keylen, const char *value, size_t vallen) { data_string *ds; - + UNUSED(srv); /* if there already is a key by this name overwrite the value */ if (NULL != (ds = (data_string *)array_get_element(con->response.headers, key))) { buffer_copy_string(ds->value, value); - + return 0; } - + + return response_header_insert(srv, con, key, keylen, value, vallen); +} + +int response_header_append(server *srv, connection *con, const char *key, size_t keylen, const char *value, size_t vallen) { + data_string *ds; + + UNUSED(srv); + + /* if there already is a key by this name append the value */ + if (NULL != (ds = (data_string *)array_get_element(con->response.headers, key))) { + buffer_append_string_len(ds->value, CONST_STR_LEN(", ")); + buffer_append_string_len(ds->value, value, vallen); + return 0; + } + return response_header_insert(srv, con, key, keylen, value, vallen); } int http_response_redirect_to_directory(server *srv, connection *con) { buffer *o; - + o = buffer_init(); - + if (con->conf.is_ssl) { - buffer_copy_string(o, "https://"); + buffer_copy_string_len(o, CONST_STR_LEN("https://")); } else { - buffer_copy_string(o, "http://"); + buffer_copy_string_len(o, CONST_STR_LEN("http://")); } if (con->uri.authority->used) { buffer_append_string_buffer(o, con->uri.authority); @@ -123,36 +138,36 @@ int http_response_redirect_to_directory(server *srv, connection *con) { #endif sock_addr our_addr; socklen_t our_addr_len; - + our_addr_len = sizeof(our_addr); - + if (-1 == getsockname(con->fd, &(our_addr.plain), &our_addr_len)) { con->http_status = 500; - + log_error_write(srv, __FILE__, __LINE__, "ss", "can't get sockname", strerror(errno)); - + buffer_free(o); return 0; } - - + + /* Lookup name: secondly try to get hostname for bind address */ switch(our_addr.plain.sa_family) { #ifdef HAVE_IPV6 case AF_INET6: - if (0 != getnameinfo((const struct sockaddr *)(&our_addr.ipv6), - SA_LEN((const struct sockaddr *)&our_addr.ipv6), + if (0 != getnameinfo((const struct sockaddr *)(&our_addr.ipv6), + SA_LEN((const struct sockaddr *)&our_addr.ipv6), hbuf, sizeof(hbuf), NULL, 0, 0)) { - + char dst[INET6_ADDRSTRLEN]; - + log_error_write(srv, __FILE__, __LINE__, - "SSSS", "NOTICE: getnameinfo failed: ", + "SSS", "NOTICE: getnameinfo failed: ", strerror(errno), ", using ip-address instead"); - - buffer_append_string(o, - inet_ntop(AF_INET6, (char *)&our_addr.ipv6.sin6_addr, + + buffer_append_string(o, + inet_ntop(AF_INET6, (char *)&our_addr.ipv6.sin6_addr, dst, sizeof(dst))); } else { buffer_append_string(o, hbuf); @@ -162,9 +177,9 @@ int http_response_redirect_to_directory(server *srv, connection *con) { case AF_INET: if (NULL == (he = gethostbyaddr((char *)&our_addr.ipv4.sin_addr, sizeof(struct in_addr), AF_INET))) { log_error_write(srv, __FILE__, __LINE__, - "SdSS", "NOTICE: gethostbyaddr failed: ", + "SdS", "NOTICE: gethostbyaddr failed: ", h_errno, ", using ip-address instead"); - + buffer_append_string(o, inet_ntoa(our_addr.ipv4.sin_addr)); } else { buffer_append_string(o, he->h_name); @@ -173,57 +188,58 @@ int http_response_redirect_to_directory(server *srv, connection *con) { default: log_error_write(srv, __FILE__, __LINE__, "S", "ERROR: unsupported address-type"); - + buffer_free(o); return -1; } - - if (!((con->conf.is_ssl == 0 && srv->srvconf.port == 80) || + + if (!((con->conf.is_ssl == 0 && srv->srvconf.port == 80) || (con->conf.is_ssl == 1 && srv->srvconf.port == 443))) { - buffer_append_string(o, ":"); + buffer_append_string_len(o, CONST_STR_LEN(":")); buffer_append_long(o, srv->srvconf.port); } } buffer_append_string_buffer(o, con->uri.path); - buffer_append_string(o, "/"); + buffer_append_string_len(o, CONST_STR_LEN("/")); if (!buffer_is_empty(con->uri.query)) { - buffer_append_string(o, "?"); + buffer_append_string_len(o, CONST_STR_LEN("?")); buffer_append_string_buffer(o, con->uri.query); } - + response_header_insert(srv, con, CONST_STR_LEN("Location"), CONST_BUF_LEN(o)); - + con->http_status = 301; - + con->file_finished = 1; + buffer_free(o); - + return 0; } buffer * strftime_cache_get(server *srv, time_t last_mod) { struct tm *tm; size_t i; - + for (i = 0; i < FILE_CACHE_MAX; i++) { /* found cache-entry */ if (srv->mtime_cache[i].mtime == last_mod) return srv->mtime_cache[i].str; - + /* found empty slot */ if (srv->mtime_cache[i].mtime == 0) break; } - + if (i == FILE_CACHE_MAX) { i = 0; } - + srv->mtime_cache[i].mtime = last_mod; buffer_prepare_copy(srv->mtime_cache[i].str, 1024); tm = gmtime(&(srv->mtime_cache[i].mtime)); - srv->mtime_cache[i].str->used = strftime(srv->mtime_cache[i].str->ptr, + srv->mtime_cache[i].str->used = strftime(srv->mtime_cache[i].str->ptr, srv->mtime_cache[i].str->size - 1, "%a, %d %b %Y %H:%M:%S GMT", tm); srv->mtime_cache[i].str->used++; - + return srv->mtime_cache[i].str; } @@ -238,56 +254,63 @@ int http_response_handle_cachable(server *srv, connection *con, buffer *mtime) { * request. That is, if no entity tags match, then the server MUST NOT * return a 304 (Not Modified) response. */ - + /* last-modified handling */ if (con->request.http_if_none_match) { if (etag_is_equal(con->physical.etag, con->request.http_if_none_match)) { - if (con->request.http_method == HTTP_METHOD_GET || + if (con->request.http_method == HTTP_METHOD_GET || con->request.http_method == HTTP_METHOD_HEAD) { - + /* check if etag + last-modified */ if (con->request.http_if_modified_since) { size_t used_len; char *semicolon; - + if (NULL == (semicolon = strchr(con->request.http_if_modified_since, ';'))) { used_len = strlen(con->request.http_if_modified_since); } else { used_len = semicolon - con->request.http_if_modified_since; } - + if (0 == strncmp(con->request.http_if_modified_since, mtime->ptr, used_len)) { - con->http_status = 304; + if ('\0' == mtime->ptr[used_len]) con->http_status = 304; return HANDLER_FINISHED; } else { char buf[sizeof("Sat, 23 Jul 2005 21:20:01 GMT")]; + time_t t_header, t_file; + struct tm tm; - /* convert to timestamp */ - if (used_len < sizeof(buf)) { - time_t t_header, t_file; - struct tm tm; - - strncpy(buf, con->request.http_if_modified_since, used_len); - buf[used_len] = '\0'; - - strptime(buf, "%a, %d %b %Y %H:%M:%S GMT", &tm); - t_header = mktime(&tm); - - strptime(mtime->ptr, "%a, %d %b %Y %H:%M:%S GMT", &tm); - t_file = mktime(&tm); - - if (t_file > t_header) { - con->http_status = 304; - return HANDLER_FINISHED; - } - } else { - log_error_write(srv, __FILE__, __LINE__, "ssdd", - "DEBUG: Last-Modified check failed as the received timestamp was too long:", + /* check if we can safely copy the string */ + if (used_len >= sizeof(buf)) { + log_error_write(srv, __FILE__, __LINE__, "ssdd", + "DEBUG: Last-Modified check failed as the received timestamp was too long:", con->request.http_if_modified_since, used_len, sizeof(buf) - 1); - + con->http_status = 412; + con->mode = DIRECT; return HANDLER_FINISHED; } + + + strncpy(buf, con->request.http_if_modified_since, used_len); + buf[used_len] = '\0'; + + if (NULL == strptime(buf, "%a, %d %b %Y %H:%M:%S GMT", &tm)) { + con->http_status = 412; + con->mode = DIRECT; + return HANDLER_FINISHED; + } + tm.tm_isdst = 0; + t_header = mktime(&tm); + + strptime(mtime->ptr, "%a, %d %b %Y %H:%M:%S GMT", &tm); + tm.tm_isdst = 0; + t_file = mktime(&tm); + + if (t_file > t_header) return HANDLER_GO_ON; + + con->http_status = 304; + return HANDLER_FINISHED; } } else { con->http_status = 304; @@ -295,20 +318,49 @@ int http_response_handle_cachable(server *srv, connection *con, buffer *mtime) { } } else { con->http_status = 412; + con->mode = DIRECT; return HANDLER_FINISHED; } } } else if (con->request.http_if_modified_since) { size_t used_len; char *semicolon; - + if (NULL == (semicolon = strchr(con->request.http_if_modified_since, ';'))) { used_len = strlen(con->request.http_if_modified_since); } else { used_len = semicolon - con->request.http_if_modified_since; } - + if (0 == strncmp(con->request.http_if_modified_since, mtime->ptr, used_len)) { + if ('\0' == mtime->ptr[used_len]) con->http_status = 304; + return HANDLER_FINISHED; + } else { + char buf[sizeof("Sat, 23 Jul 2005 21:20:01 GMT")]; + time_t t_header, t_file; + struct tm tm; + + /* convert to timestamp */ + if (used_len >= sizeof(buf)) return HANDLER_GO_ON; + + strncpy(buf, con->request.http_if_modified_since, used_len); + buf[used_len] = '\0'; + + if (NULL == strptime(buf, "%a, %d %b %Y %H:%M:%S GMT", &tm)) { + /** + * parsing failed, let's get out of here + */ + return HANDLER_GO_ON; + } + tm.tm_isdst = 0; + t_header = mktime(&tm); + + strptime(mtime->ptr, "%a, %d %b %Y %H:%M:%S GMT", &tm); + tm.tm_isdst = 0; + t_file = mktime(&tm); + + if (t_file > t_header) return HANDLER_GO_ON; + con->http_status = 304; return HANDLER_FINISHED; } diff --git a/src/http_auth.c b/src/http_auth.c index 9976c15..d7d246b 100644 --- a/src/http_auth.c +++ b/src/http_auth.c @@ -1,6 +1,8 @@ -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif +#include "server.h" +#include "log.h" +#include "http_auth.h" +#include "inet_ntop_cache.h" +#include "stream.h" #ifdef HAVE_CRYPT_H # include <crypt.h> @@ -25,49 +27,61 @@ #include <unistd.h> #include <ctype.h> -#include "server.h" -#include "log.h" -#include "http_auth.h" -#include "http_auth_digest.h" -#include "stream.h" +#include "md5.h" -#ifdef USE_OPENSSL -# include <openssl/md5.h> -#else -# include "md5.h" -#endif +#define HASHLEN 16 +#define HASHHEXLEN 32 +typedef unsigned char HASH[HASHLEN]; +typedef char HASHHEX[HASHHEXLEN+1]; +static void CvtHex(const HASH Bin, char Hex[33]) { + unsigned short i; -#ifdef USE_PAM -#include <security/pam_appl.h> -#include <security/pam_misc.h> + for (i = 0; i < 16; i++) { + Hex[i*2] = int2hex((Bin[i] >> 4) & 0xf); + Hex[i*2+1] = int2hex(Bin[i] & 0xf); + } + Hex[32] = '\0'; +} -static struct pam_conv conv = { - misc_conv, - NULL -}; -#endif +/** + * the $apr1$ handling is taken from apache 1.3.x + */ + +/* + * The apr_md5_encode() routine uses much code obtained from the FreeBSD 3.0 + * MD5 crypt() function, which is licenced as follows: + * ---------------------------------------------------------------------------- + * "THE BEER-WARE LICENSE" (Revision 42): + * <phk@login.dknet.dk> wrote this file. As long as you retain this notice you + * can do whatever you want with this stuff. If we meet some day, and you think + * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp + * ---------------------------------------------------------------------------- + */ handler_t auth_ldap_init(server *srv, mod_auth_plugin_config *s); static const char base64_pad = '='; +/* "A-Z a-z 0-9 + /" maps to 0-63 */ static const short base64_reverse_table[256] = { - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, - 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, - -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, - 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, - -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, - 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 +/* 0 1 2 3 4 5 6 7 8 9 A B C D E F */ + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x00 - 0x0F */ + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x10 - 0x1F */ + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, /* 0x20 - 0x2F */ + 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, /* 0x30 - 0x3F */ + -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, /* 0x40 - 0x4F */ + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, /* 0x50 - 0x5F */ + -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, /* 0x60 - 0x6F */ + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1, /* 0x70 - 0x7F */ + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x80 - 0x8F */ + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x90 - 0x9F */ + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0xA0 - 0xAF */ + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0xB0 - 0xBF */ + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0xC0 - 0xCF */ + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0xD0 - 0xDF */ + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0xE0 - 0xEF */ + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0xF0 - 0xFF */ }; @@ -75,25 +89,25 @@ static unsigned char * base64_decode(buffer *out, const char *in) { unsigned char *result; int ch, j = 0, k; size_t i; - + size_t in_len = strlen(in); - + buffer_prepare_copy(out, in_len); - + result = (unsigned char *)out->ptr; - + ch = in[0]; /* run through the whole string, converting as we go */ for (i = 0; i < in_len; i++) { - ch = in[i]; - + ch = (unsigned char) in[i]; + if (ch == '\0') break; - + if (ch == base64_pad) break; - + ch = base64_reverse_table[ch]; if (ch < 0) continue; - + switch(i % 4) { case 0: result[j] = ch << 2; @@ -125,168 +139,168 @@ static unsigned char * base64_decode(buffer *out, const char *in) { } } result[k] = '\0'; - + out->used = k; - + return result; } static int http_auth_get_password(server *srv, mod_auth_plugin_data *p, buffer *username, buffer *realm, buffer *password) { int ret = -1; - + if (!username->used|| !realm->used) return -1; - + if (p->conf.auth_backend == AUTH_BACKEND_HTDIGEST) { stream f; char * f_line; - + if (buffer_is_empty(p->conf.auth_htdigest_userfile)) return -1; - + if (0 != stream_open(&f, p->conf.auth_htdigest_userfile)) { log_error_write(srv, __FILE__, __LINE__, "sbss", "opening digest-userfile", p->conf.auth_htdigest_userfile, "failed:", strerror(errno)); - + return -1; } - + f_line = f.start; - + while (f_line - f.start != f.size) { char *f_user, *f_pwd, *e, *f_realm; size_t u_len, pwd_len, r_len; - + f_user = f_line; - - /* + + /* * htdigest format - * - * user:realm:md5(user:realm:password) + * + * user:realm:md5(user:realm:password) */ - + if (NULL == (f_realm = memchr(f_user, ':', f.size - (f_user - f.start) ))) { - log_error_write(srv, __FILE__, __LINE__, "sbs", - "parsed error in", p->conf.auth_htdigest_userfile, + log_error_write(srv, __FILE__, __LINE__, "sbs", + "parsed error in", p->conf.auth_htdigest_userfile, "expected 'username:realm:hashed password'"); - + stream_close(&f); - + return -1; } - + if (NULL == (f_pwd = memchr(f_realm + 1, ':', f.size - (f_realm + 1 - f.start)))) { - log_error_write(srv, __FILE__, __LINE__, "sbs", - "parsed error in", p->conf.auth_plain_userfile, + log_error_write(srv, __FILE__, __LINE__, "sbs", + "parsed error in", p->conf.auth_plain_userfile, "expected 'username:realm:hashed password'"); - + stream_close(&f); - + return -1; } - + /* get pointers to the fields */ - u_len = f_realm - f_user; + u_len = f_realm - f_user; f_realm++; r_len = f_pwd - f_realm; f_pwd++; - + if (NULL != (e = memchr(f_pwd, '\n', f.size - (f_pwd - f.start)))) { pwd_len = e - f_pwd; } else { pwd_len = f.size - (f_pwd - f.start); } - + if (username->used - 1 == u_len && (realm->used - 1 == r_len) && (0 == strncmp(username->ptr, f_user, u_len)) && (0 == strncmp(realm->ptr, f_realm, r_len))) { /* found */ - + buffer_copy_string_len(password, f_pwd, pwd_len); - + ret = 0; break; } - + /* EOL */ if (!e) break; - + f_line = e + 1; } - + stream_close(&f); } else if (p->conf.auth_backend == AUTH_BACKEND_HTPASSWD || p->conf.auth_backend == AUTH_BACKEND_PLAIN) { stream f; char * f_line; buffer *auth_fn; - + auth_fn = (p->conf.auth_backend == AUTH_BACKEND_HTPASSWD) ? p->conf.auth_htpasswd_userfile : p->conf.auth_plain_userfile; - + if (buffer_is_empty(auth_fn)) return -1; - + if (0 != stream_open(&f, auth_fn)) { - log_error_write(srv, __FILE__, __LINE__, "sbss", + log_error_write(srv, __FILE__, __LINE__, "sbss", "opening plain-userfile", auth_fn, "failed:", strerror(errno)); - + return -1; } - + f_line = f.start; - + while (f_line - f.start != f.size) { char *f_user, *f_pwd, *e; size_t u_len, pwd_len; - + f_user = f_line; - - /* + + /* * htpasswd format - * + * * user:crypted passwd */ - + if (NULL == (f_pwd = memchr(f_user, ':', f.size - (f_user - f.start) ))) { - log_error_write(srv, __FILE__, __LINE__, "sbs", - "parsed error in", auth_fn, + log_error_write(srv, __FILE__, __LINE__, "sbs", + "parsed error in", auth_fn, "expected 'username:hashed password'"); - + stream_close(&f); - + return -1; } - + /* get pointers to the fields */ - u_len = f_pwd - f_user; + u_len = f_pwd - f_user; f_pwd++; - + if (NULL != (e = memchr(f_pwd, '\n', f.size - (f_pwd - f.start)))) { pwd_len = e - f_pwd; } else { pwd_len = f.size - (f_pwd - f.start); } - + if (username->used - 1 == u_len && (0 == strncmp(username->ptr, f_user, u_len))) { /* found */ - + buffer_copy_string_len(password, f_pwd, pwd_len); - + ret = 0; break; } - + /* EOL */ if (!e) break; - + f_line = e + 1; } - + stream_close(&f); } else if (p->conf.auth_backend == AUTH_BACKEND_LDAP) { ret = 0; } else { return -1; } - + return ret; } @@ -296,7 +310,7 @@ static int http_auth_match_rules(server *srv, mod_auth_plugin_data *p, const cha int username_len; data_string *require; array *req; - + UNUSED(group); UNUSED(host); @@ -304,12 +318,12 @@ static int http_auth_match_rules(server *srv, mod_auth_plugin_data *p, const cha /* search auth-directives for path */ for (i = 0; i < p->conf.auth_require->used; i++) { if (p->conf.auth_require->data[i]->key->used == 0) continue; - + if (0 == strncmp(url, p->conf.auth_require->data[i]->key->ptr, p->conf.auth_require->data[i]->key->used - 1)) { break; } } - + if (i == p->conf.auth_require->used) { return -1; } @@ -317,72 +331,72 @@ static int http_auth_match_rules(server *srv, mod_auth_plugin_data *p, const cha req = ((data_array *)(p->conf.auth_require->data[i]))->value; require = (data_string *)array_get_element(req, "require"); - + /* if we get here, the user we got a authed user */ if (0 == strcmp(require->value->ptr, "valid-user")) { return 0; } - + /* user=name1|group=name3|host=name4 */ - + /* seperate the string by | */ #if 0 log_error_write(srv, __FILE__, __LINE__, "sb", "rules", require->value); -#endif - +#endif + username_len = username ? strlen(username) : 0; - + r = rules = require->value->ptr; - + while (1) { const char *eq; const char *k, *v, *e; int k_len, v_len, r_len; - + e = strchr(r, '|'); - + if (e) { r_len = e - r; } else { r_len = strlen(rules) - (r - rules); } - + /* from r to r + r_len is a rule */ - + if (0 == strncmp(r, "valid-user", r_len)) { - log_error_write(srv, __FILE__, __LINE__, "sb", + log_error_write(srv, __FILE__, __LINE__, "sb", "parsing the 'require' section in 'auth.require' failed: valid-user cannot be combined with other require rules", require->value); return -1; } - + /* search for = in the rules */ if (NULL == (eq = strchr(r, '='))) { - log_error_write(srv, __FILE__, __LINE__, "sb", - "parsing the 'require' section in 'auth.require' failed: a = is missing", + log_error_write(srv, __FILE__, __LINE__, "sb", + "parsing the 'require' section in 'auth.require' failed: a = is missing", require->value); return -1; } - + /* = out of range */ if (eq > r + r_len) { - log_error_write(srv, __FILE__, __LINE__, "sb", + log_error_write(srv, __FILE__, __LINE__, "sb", "parsing the 'require' section in 'auth.require' failed: = out of range", require->value); - + return -1; } - + /* the part before the = is user|group|host */ - + k = r; k_len = eq - r; v = eq + 1; v_len = r_len - k_len - 1; - + if (k_len == 4) { if (0 == strncmp(k, "user", k_len)) { - if (username && + if (username && username_len == v_len && 0 == strncmp(username, v, v_len)) { return 0; @@ -404,19 +418,191 @@ static int http_auth_match_rules(server *srv, mod_auth_plugin_data *p, const cha log_error_write(srv, __FILE__, __LINE__, "s", "unknown key"); return -1; } - + if (!e) break; r = e + 1; } - + log_error_write(srv, __FILE__, __LINE__, "s", "nothing matched"); - + return -1; } +#define APR_MD5_DIGESTSIZE 16 +#define APR1_ID "$apr1$" + +/* + * The following MD5 password encryption code was largely borrowed from + * the FreeBSD 3.0 /usr/src/lib/libcrypt/crypt.c file, which is + * licenced as stated at the top of this file. + */ + +static void to64(char *s, unsigned long v, int n) +{ + static const unsigned char itoa64[] = /* 0 ... 63 => ASCII - 64 */ + "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; + + while (--n >= 0) { + *s++ = itoa64[v&0x3f]; + v >>= 6; + } +} + +static void apr_md5_encode(const char *pw, const char *salt, char *result, size_t nbytes) { + /* + * Minimum size is 8 bytes for salt, plus 1 for the trailing NUL, + * plus 4 for the '$' separators, plus the password hash itself. + * Let's leave a goodly amount of leeway. + */ + + char passwd[120], *p; + const char *sp, *ep; + unsigned char final[APR_MD5_DIGESTSIZE]; + ssize_t sl, pl, i; + li_MD5_CTX ctx, ctx1; + unsigned long l; + + /* + * Refine the salt first. It's possible we were given an already-hashed + * string as the salt argument, so extract the actual salt value from it + * if so. Otherwise just use the string up to the first '$' as the salt. + */ + sp = salt; + + /* + * If it starts with the magic string, then skip that. + */ + if (!strncmp(sp, APR1_ID, strlen(APR1_ID))) { + sp += strlen(APR1_ID); + } + + /* + * It stops at the first '$' or 8 chars, whichever comes first + */ + for (ep = sp; (*ep != '\0') && (*ep != '$') && (ep < (sp + 8)); ep++) { + continue; + } + + /* + * Get the length of the true salt + */ + sl = ep - sp; + + /* + * 'Time to make the doughnuts..' + */ + li_MD5_Init(&ctx); + + /* + * The password first, since that is what is most unknown + */ + li_MD5_Update(&ctx, pw, strlen(pw)); + + /* + * Then our magic string + */ + li_MD5_Update(&ctx, APR1_ID, strlen(APR1_ID)); + + /* + * Then the raw salt + */ + li_MD5_Update(&ctx, sp, sl); + + /* + * Then just as many characters of the MD5(pw, salt, pw) + */ + li_MD5_Init(&ctx1); + li_MD5_Update(&ctx1, pw, strlen(pw)); + li_MD5_Update(&ctx1, sp, sl); + li_MD5_Update(&ctx1, pw, strlen(pw)); + li_MD5_Final(final, &ctx1); + for (pl = strlen(pw); pl > 0; pl -= APR_MD5_DIGESTSIZE) { + li_MD5_Update(&ctx, final, + (pl > APR_MD5_DIGESTSIZE) ? APR_MD5_DIGESTSIZE : pl); + } + + /* + * Don't leave anything around in vm they could use. + */ + memset(final, 0, sizeof(final)); + + /* + * Then something really weird... + */ + for (i = strlen(pw); i != 0; i >>= 1) { + if (i & 1) { + li_MD5_Update(&ctx, final, 1); + } + else { + li_MD5_Update(&ctx, pw, 1); + } + } + + /* + * Now make the output string. We know our limitations, so we + * can use the string routines without bounds checking. + */ + strcpy(passwd, APR1_ID); + strncat(passwd, sp, sl); + strcat(passwd, "$"); + + li_MD5_Final(final, &ctx); + + /* + * And now, just to make sure things don't run too fast.. + * On a 60 Mhz Pentium this takes 34 msec, so you would + * need 30 seconds to build a 1000 entry dictionary... + */ + for (i = 0; i < 1000; i++) { + li_MD5_Init(&ctx1); + if (i & 1) { + li_MD5_Update(&ctx1, pw, strlen(pw)); + } + else { + li_MD5_Update(&ctx1, final, APR_MD5_DIGESTSIZE); + } + if (i % 3) { + li_MD5_Update(&ctx1, sp, sl); + } + + if (i % 7) { + li_MD5_Update(&ctx1, pw, strlen(pw)); + } + + if (i & 1) { + li_MD5_Update(&ctx1, final, APR_MD5_DIGESTSIZE); + } + else { + li_MD5_Update(&ctx1, pw, strlen(pw)); + } + li_MD5_Final(final,&ctx1); + } + + p = passwd + strlen(passwd); + + l = (final[ 0]<<16) | (final[ 6]<<8) | final[12]; to64(p, l, 4); p += 4; + l = (final[ 1]<<16) | (final[ 7]<<8) | final[13]; to64(p, l, 4); p += 4; + l = (final[ 2]<<16) | (final[ 8]<<8) | final[14]; to64(p, l, 4); p += 4; + l = (final[ 3]<<16) | (final[ 9]<<8) | final[15]; to64(p, l, 4); p += 4; + l = (final[ 4]<<16) | (final[10]<<8) | final[ 5]; to64(p, l, 4); p += 4; + l = final[11] ; to64(p, l, 2); p += 2; + *p = '\0'; + + /* + * Don't leave anything around in vm they could use. + */ + memset(final, 0, sizeof(final)); + + /* FIXME + */ +#define apr_cpystrn strncpy + apr_cpystrn(result, passwd, nbytes - 1); +} + + /** - * - * + * + * * @param password password-string from the auth-backend * @param pw password-string from the client */ @@ -426,41 +612,49 @@ static int http_auth_basic_password_compare(server *srv, mod_auth_plugin_data *p UNUSED(req); if (p->conf.auth_backend == AUTH_BACKEND_HTDIGEST) { - /* + /* * htdigest format - * - * user:realm:md5(user:realm:password) + * + * user:realm:md5(user:realm:password) */ - - MD5_CTX Md5Ctx; + + li_MD5_CTX Md5Ctx; HASH HA1; char a1[256]; - - MD5_Init(&Md5Ctx); - MD5_Update(&Md5Ctx, (unsigned char *)username->ptr, username->used - 1); - MD5_Update(&Md5Ctx, (unsigned char *)":", 1); - MD5_Update(&Md5Ctx, (unsigned char *)realm->ptr, realm->used - 1); - MD5_Update(&Md5Ctx, (unsigned char *)":", 1); - MD5_Update(&Md5Ctx, (unsigned char *)pw, strlen(pw)); - MD5_Final(HA1, &Md5Ctx); - + + li_MD5_Init(&Md5Ctx); + li_MD5_Update(&Md5Ctx, (unsigned char *)username->ptr, username->used - 1); + li_MD5_Update(&Md5Ctx, (unsigned char *)":", 1); + li_MD5_Update(&Md5Ctx, (unsigned char *)realm->ptr, realm->used - 1); + li_MD5_Update(&Md5Ctx, (unsigned char *)":", 1); + li_MD5_Update(&Md5Ctx, (unsigned char *)pw, strlen(pw)); + li_MD5_Final(HA1, &Md5Ctx); + CvtHex(HA1, a1); - + if (0 == strcmp(password->ptr, a1)) { return 0; } - } else if (p->conf.auth_backend == AUTH_BACKEND_HTPASSWD) { -#ifdef HAVE_CRYPT + } else if (p->conf.auth_backend == AUTH_BACKEND_HTPASSWD) { + char sample[120]; + if (!strncmp(password->ptr, APR1_ID, strlen(APR1_ID))) { + /* + * The hash was created using $apr1$ custom algorithm. + */ + apr_md5_encode(pw, password->ptr, sample, sizeof(sample)); + return (strcmp(sample, password->ptr) == 0) ? 0 : 1; + } else { +#ifdef HAVE_CRYPT char salt[32]; char *crypted; size_t salt_len = 0; - /* + /* * htpasswd format - * + * * user:crypted password */ - /* + /* * Algorithm Salt * CRYPT_STD_DES 2-character (Default) * CRYPT_EXT_DES 9-character @@ -469,7 +663,6 @@ static int http_auth_basic_password_compare(server *srv, mod_auth_plugin_data *p */ if (password->used < 13 + 1) { - fprintf(stderr, "%s.%d\n", __FILE__, __LINE__); return -1; } @@ -478,9 +671,8 @@ static int http_auth_basic_password_compare(server *srv, mod_auth_plugin_data *p salt_len = 2; } else if (password->ptr[0] == '$' && password->ptr[2] == '$') { char *dollar = NULL; - + if (NULL == (dollar = strchr(password->ptr + 3, '$'))) { - fprintf(stderr, "%s.%d\n", __FILE__, __LINE__); return -1; } @@ -488,55 +680,26 @@ static int http_auth_basic_password_compare(server *srv, mod_auth_plugin_data *p } if (salt_len > sizeof(salt) - 1) { - fprintf(stderr, "%s.%d\n", __FILE__, __LINE__); return -1; } strncpy(salt, password->ptr, salt_len); salt[salt_len] = '\0'; - + crypted = crypt(pw, salt); if (0 == strcmp(password->ptr, crypted)) { return 0; - } else { - fprintf(stderr, "%s.%d\n", __FILE__, __LINE__); } - -#endif - } else if (p->conf.auth_backend == AUTH_BACKEND_PLAIN) { + +#endif + } + } else if (p->conf.auth_backend == AUTH_BACKEND_PLAIN) { if (0 == strcmp(password->ptr, pw)) { return 0; } - } else if (p->conf.auth_backend == AUTH_BACKEND_PAM) { -#ifdef USE_PAM - pam_handle_t *pamh=NULL; - int retval; - - retval = pam_start("lighttpd", username->ptr, &conv, &pamh); - - if (retval == PAM_SUCCESS) - retval = pam_authenticate(pamh, 0); /* is user really user? */ - - if (retval == PAM_SUCCESS) - retval = pam_acct_mgmt(pamh, 0); /* permitted access? */ - - /* This is where we have been authorized or not. */ - - if (pam_end(pamh,retval) != PAM_SUCCESS) { /* close Linux-PAM */ - pamh = NULL; - log_error_write(srv, __FILE__, __LINE__, "s", "failed to release authenticator"); - } - - if (retval == PAM_SUCCESS) { - log_error_write(srv, __FILE__, __LINE__, "s", "Authenticated"); - return 0; - } else { - log_error_write(srv, __FILE__, __LINE__, "s", "Not Authenticated"); - } -#endif - } else if (p->conf.auth_backend == AUTH_BACKEND_LDAP) { + } else if (p->conf.auth_backend == AUTH_BACKEND_LDAP) { #ifdef USE_LDAP LDAP *ldap; LDAPMessage *lm, *first; @@ -544,117 +707,129 @@ static int http_auth_basic_password_compare(server *srv, mod_auth_plugin_data *p int ret; char *attrs[] = { LDAP_NO_ATTRS, NULL }; size_t i; - + /* for now we stay synchronous */ - - /* + + /* * 1. connect anonymously (done in plugin init) * 2. get DN for uid = username * 3. auth against ldap server * 4. (optional) check a field * 5. disconnect - * + * */ - + /* check username - * + * * we have to protect us againt username which modifies out filter in * a unpleasant way */ - + for (i = 0; i < username->used - 1; i++) { char c = username->ptr[i]; - + if (!isalpha(c) && - !isdigit(c)) { - - log_error_write(srv, __FILE__, __LINE__, "sbd", - "ldap: invalid character (a-zA-Z0-9 allowed) in username:", username, i); - + !isdigit(c) && + (c != ' ') && + (c != '@') && + (c != '-') && + (c != '_') && + (c != '.') ) { + + log_error_write(srv, __FILE__, __LINE__, "sbd", + "ldap: invalid character (- _.@a-zA-Z0-9 allowed) in username:", username, i); + return -1; } } - - - + + if (p->conf.auth_ldap_allow_empty_pw != 1 && pw[0] == '\0') + return -1; + /* build filter */ buffer_copy_string_buffer(p->ldap_filter, p->conf.ldap_filter_pre); buffer_append_string_buffer(p->ldap_filter, username); buffer_append_string_buffer(p->ldap_filter, p->conf.ldap_filter_post); - - + + /* 2. */ - if (p->conf.ldap == NULL || - LDAP_SUCCESS != (ret = ldap_search_s(p->conf.ldap, p->conf.auth_ldap_basedn->ptr, LDAP_SCOPE_SUBTREE, p->ldap_filter->ptr, attrs, 0, &lm))) { - if (auth_ldap_init(srv, &p->conf) != HANDLER_GO_ON) - return -1; - if (LDAP_SUCCESS != (ret = ldap_search_s(p->conf.ldap, p->conf.auth_ldap_basedn->ptr, LDAP_SCOPE_SUBTREE, p->ldap_filter->ptr, attrs, 0, &lm))) { + if (p->anon_conf->ldap == NULL || + LDAP_SUCCESS != (ret = ldap_search_s(p->anon_conf->ldap, p->conf.auth_ldap_basedn->ptr, LDAP_SCOPE_SUBTREE, p->ldap_filter->ptr, attrs, 0, &lm))) { - log_error_write(srv, __FILE__, __LINE__, "sssb", - "ldap:", ldap_err2string(ret), "filter:", p->ldap_filter); - - return -1; + /* try again; the ldap library sometimes fails for the first call but reconnects */ + if (p->anon_conf->ldap == NULL || ret != LDAP_SERVER_DOWN || + LDAP_SUCCESS != (ret = ldap_search_s(p->anon_conf->ldap, p->conf.auth_ldap_basedn->ptr, LDAP_SCOPE_SUBTREE, p->ldap_filter->ptr, attrs, 0, &lm))) { + + if (auth_ldap_init(srv, p->anon_conf) != HANDLER_GO_ON) + return -1; + + if (p->anon_conf->ldap == NULL || + LDAP_SUCCESS != (ret = ldap_search_s(p->anon_conf->ldap, p->conf.auth_ldap_basedn->ptr, LDAP_SCOPE_SUBTREE, p->ldap_filter->ptr, attrs, 0, &lm))) { + log_error_write(srv, __FILE__, __LINE__, "sssb", + "ldap:", ldap_err2string(ret), "filter:", p->ldap_filter); + return -1; + } } } - - if (NULL == (first = ldap_first_entry(p->conf.ldap, lm))) { + + if (NULL == (first = ldap_first_entry(p->anon_conf->ldap, lm))) { log_error_write(srv, __FILE__, __LINE__, "s", "ldap ..."); - + ldap_msgfree(lm); - + return -1; } - - if (NULL == (dn = ldap_get_dn(p->conf.ldap, first))) { + + if (NULL == (dn = ldap_get_dn(p->anon_conf->ldap, first))) { log_error_write(srv, __FILE__, __LINE__, "s", "ldap ..."); - + ldap_msgfree(lm); - + return -1; } - + ldap_msgfree(lm); - - + + /* 3. */ if (NULL == (ldap = ldap_init(p->conf.auth_ldap_hostname->ptr, LDAP_PORT))) { log_error_write(srv, __FILE__, __LINE__, "ss", "ldap ...", strerror(errno)); return -1; } - + ret = LDAP_VERSION3; if (LDAP_OPT_SUCCESS != (ret = ldap_set_option(ldap, LDAP_OPT_PROTOCOL_VERSION, &ret))) { log_error_write(srv, __FILE__, __LINE__, "ss", "ldap:", ldap_err2string(ret)); - + ldap_unbind_s(ldap); - + return -1; } - + if (p->conf.auth_ldap_starttls == 1) { if (LDAP_OPT_SUCCESS != (ret = ldap_start_tls_s(ldap, NULL, NULL))) { log_error_write(srv, __FILE__, __LINE__, "ss", "ldap startTLS failed:", ldap_err2string(ret)); - + ldap_unbind_s(ldap); - + return -1; } } - + if (LDAP_SUCCESS != (ret = ldap_simple_bind_s(ldap, dn, pw))) { log_error_write(srv, __FILE__, __LINE__, "ss", "ldap:", ldap_err2string(ret)); - + ldap_unbind_s(ldap); - + return -1; } - + /* 5. */ ldap_unbind_s(ldap); - + /* everything worked, good, access granted */ - + return 0; #endif } @@ -664,65 +839,73 @@ static int http_auth_basic_password_compare(server *srv, mod_auth_plugin_data *p int http_auth_basic_check(server *srv, connection *con, mod_auth_plugin_data *p, array *req, buffer *url, const char *realm_str) { buffer *username, *password; char *pw; - + data_string *realm; - + realm = (data_string *)array_get_element(req, "realm"); - + username = buffer_init(); - password = buffer_init(); - - base64_decode(username, realm_str); - + + if (!base64_decode(username, realm_str)) { + log_error_write(srv, __FILE__, __LINE__, "sb", "decodeing base64-string failed", username); + + buffer_free(username); + return 0; + } + /* r2 == user:password */ if (NULL == (pw = strchr(username->ptr, ':'))) { - buffer_free(username); - log_error_write(srv, __FILE__, __LINE__, "sb", ": is missing in", username); - + + buffer_free(username); return 0; } - + *pw++ = '\0'; - + username->used = pw - username->ptr; - + + password = buffer_init(); /* copy password to r1 */ if (http_auth_get_password(srv, p, username, realm->value, password)) { buffer_free(username); buffer_free(password); - - log_error_write(srv, __FILE__, __LINE__, "s", "get_password failed"); - + + if (AUTH_BACKEND_UNSET == p->conf.auth_backend) { + log_error_write(srv, __FILE__, __LINE__, "s", "auth.backend is not set"); + } else { + log_error_write(srv, __FILE__, __LINE__, "ss", "get_password failed, IP:", inet_ntop_cache_get_ip(srv, &(con->dst_addr))); + } + return 0; } - + /* password doesn't match */ if (http_auth_basic_password_compare(srv, p, req, username, realm->value, password, pw)) { - log_error_write(srv, __FILE__, __LINE__, "sbb", "password doesn't match for", con->uri.path, username); - + log_error_write(srv, __FILE__, __LINE__, "sbsBss", "password doesn't match for", con->uri.path, "username:", username, ", IP:", inet_ntop_cache_get_ip(srv, &(con->dst_addr))); + buffer_free(username); buffer_free(password); - + return 0; } - + /* value is our allow-rules */ if (http_auth_match_rules(srv, p, url->ptr, username->ptr, NULL, NULL)) { buffer_free(username); buffer_free(password); - + log_error_write(srv, __FILE__, __LINE__, "s", "rules didn't match"); - + return 0; } - + /* remember the username */ buffer_copy_string_buffer(p->auth_user, username); - + buffer_free(username); buffer_free(password); - + return 1; } @@ -735,28 +918,28 @@ typedef struct { int http_auth_digest_check(server *srv, connection *con, mod_auth_plugin_data *p, array *req, buffer *url, const char *realm_str) { char a1[256]; char a2[256]; - - char *username; - char *realm; - char *nonce; - char *uri; - char *algorithm; - char *qop; - char *cnonce; - char *nc; - char *respons; - + + char *username = NULL; + char *realm = NULL; + char *nonce = NULL; + char *uri = NULL; + char *algorithm = NULL; + char *qop = NULL; + char *cnonce = NULL; + char *nc = NULL; + char *respons = NULL; + char *e, *c; const char *m = NULL; int i; buffer *password, *b, *username_buf, *realm_buf; - - MD5_CTX Md5Ctx; + + li_MD5_CTX Md5Ctx; HASH HA1; HASH HA2; HASH RespHash; HASHHEX HA2Hex; - + /* init pointers */ #define S(x) \ @@ -771,11 +954,11 @@ int http_auth_digest_check(server *srv, connection *con, mod_auth_plugin_data *p { S("cnonce=") }, { S("nc=") }, { S("response=") }, - + { NULL, 0, NULL } }; #undef S - + dkv[0].ptr = &username; dkv[1].ptr = &realm; dkv[2].ptr = &nonce; @@ -785,41 +968,39 @@ int http_auth_digest_check(server *srv, connection *con, mod_auth_plugin_data *p dkv[6].ptr = &cnonce; dkv[7].ptr = &nc; dkv[8].ptr = &respons; - dkv[9].ptr = NULL; - + UNUSED(req); - - for (i = 0; dkv[i].key; i++) { - *(dkv[i].ptr) = NULL; - } - - + if (p->conf.auth_backend != AUTH_BACKEND_HTDIGEST && p->conf.auth_backend != AUTH_BACKEND_PLAIN) { - log_error_write(srv, __FILE__, __LINE__, "s", + log_error_write(srv, __FILE__, __LINE__, "s", "digest: unsupported backend (only htdigest or plain)"); - + return -1; } - + b = buffer_init_string(realm_str); - + /* parse credentials from client */ for (c = b->ptr; *c; c++) { + /* skip whitespaces */ + while (*c == ' ' || *c == '\t') c++; + if (!*c) break; + for (i = 0; dkv[i].key; i++) { if ((0 == strncmp(c, dkv[i].key, dkv[i].key_len))) { - if ((c[dkv[i].key_len] == '"') && + if ((c[dkv[i].key_len] == '"') && (NULL != (e = strchr(c + dkv[i].key_len + 1, '"')))) { /* value with "..." */ *(dkv[i].ptr) = c + dkv[i].key_len + 1; c = e; - + *e = '\0'; } else if (NULL != (e = strchr(c + dkv[i].key_len, ','))) { /* value without "...", terminated by ',' */ *(dkv[i].ptr) = c + dkv[i].key_len; c = e; - + *e = '\0'; } else { /* value without "...", terminated by EOL */ @@ -829,34 +1010,49 @@ int http_auth_digest_check(server *srv, connection *con, mod_auth_plugin_data *p } } } - + if (p->conf.auth_debug > 1) { log_error_write(srv, __FILE__, __LINE__, "ss", "username", username); log_error_write(srv, __FILE__, __LINE__, "ss", "realm", realm); log_error_write(srv, __FILE__, __LINE__, "ss", "nonce", nonce); log_error_write(srv, __FILE__, __LINE__, "ss", "uri", uri); - log_error_write(srv, __FILE__, __LINE__, "ss", "algorigthm", algorithm); + log_error_write(srv, __FILE__, __LINE__, "ss", "algorithm", algorithm); log_error_write(srv, __FILE__, __LINE__, "ss", "qop", qop); log_error_write(srv, __FILE__, __LINE__, "ss", "cnonce", cnonce); log_error_write(srv, __FILE__, __LINE__, "ss", "nc", nc); log_error_write(srv, __FILE__, __LINE__, "ss", "response", respons); } - + /* check if everything is transmitted */ - if (!username || + if (!username || !realm || !nonce || !uri || - (qop && !nc && !cnonce) || + (qop && (!nc || !cnonce)) || !respons ) { /* missing field */ - - log_error_write(srv, __FILE__, __LINE__, "s", + + log_error_write(srv, __FILE__, __LINE__, "s", "digest: missing field"); + + buffer_free(b); + return -1; + } + + /** + * protect the md5-sess against missing cnonce and nonce + */ + if (algorithm && + 0 == strcasecmp(algorithm, "md5-sess") && + (!nonce || !cnonce)) { + log_error_write(srv, __FILE__, __LINE__, "s", + "digest: (md5-sess: missing field"); + + buffer_free(b); return -1; } - m = get_http_method_name(con->request.http_method); + m = get_http_method_name(con->request.http_method); /* password-string == HA1 */ password = buffer_init(); @@ -869,108 +1065,110 @@ int http_auth_digest_check(server *srv, connection *con, mod_auth_plugin_data *p buffer_free(realm_buf); return 0; } - + buffer_free(username_buf); buffer_free(realm_buf); - + if (p->conf.auth_backend == AUTH_BACKEND_PLAIN) { /* generate password from plain-text */ - MD5_Init(&Md5Ctx); - MD5_Update(&Md5Ctx, (unsigned char *)username, strlen(username)); - MD5_Update(&Md5Ctx, (unsigned char *)":", 1); - MD5_Update(&Md5Ctx, (unsigned char *)realm, strlen(realm)); - MD5_Update(&Md5Ctx, (unsigned char *)":", 1); - MD5_Update(&Md5Ctx, (unsigned char *)password->ptr, password->used - 1); - MD5_Final(HA1, &Md5Ctx); + li_MD5_Init(&Md5Ctx); + li_MD5_Update(&Md5Ctx, (unsigned char *)username, strlen(username)); + li_MD5_Update(&Md5Ctx, (unsigned char *)":", 1); + li_MD5_Update(&Md5Ctx, (unsigned char *)realm, strlen(realm)); + li_MD5_Update(&Md5Ctx, (unsigned char *)":", 1); + li_MD5_Update(&Md5Ctx, (unsigned char *)password->ptr, password->used - 1); + li_MD5_Final(HA1, &Md5Ctx); } else if (p->conf.auth_backend == AUTH_BACKEND_HTDIGEST) { /* HA1 */ /* transform the 32-byte-hex-md5 to a 16-byte-md5 */ for (i = 0; i < HASHLEN; i++) { - HA1[i] = hex2int(password->ptr[i*2]) << 4; - HA1[i] |= hex2int(password->ptr[i*2+1]); + HA1[i] = hex2int(password->ptr[i*2]) << 4; + HA1[i] |= hex2int(password->ptr[i*2+1]); } } else { /* we already check that above */ SEGFAULT(); } - + buffer_free(password); - + if (algorithm && strcasecmp(algorithm, "md5-sess") == 0) { - MD5_Init(&Md5Ctx); - MD5_Update(&Md5Ctx, (unsigned char *)HA1, 16); - MD5_Update(&Md5Ctx, (unsigned char *)":", 1); - MD5_Update(&Md5Ctx, (unsigned char *)nonce, strlen(nonce)); - MD5_Update(&Md5Ctx, (unsigned char *)":", 1); - MD5_Update(&Md5Ctx, (unsigned char *)cnonce, strlen(cnonce)); - MD5_Final(HA1, &Md5Ctx); + li_MD5_Init(&Md5Ctx); + /* Errata ID 1649: http://www.rfc-editor.org/errata_search.php?rfc=2617 */ + CvtHex(HA1, a1); + li_MD5_Update(&Md5Ctx, (unsigned char *)a1, 32); + li_MD5_Update(&Md5Ctx, (unsigned char *)":", 1); + li_MD5_Update(&Md5Ctx, (unsigned char *)nonce, strlen(nonce)); + li_MD5_Update(&Md5Ctx, (unsigned char *)":", 1); + li_MD5_Update(&Md5Ctx, (unsigned char *)cnonce, strlen(cnonce)); + li_MD5_Final(HA1, &Md5Ctx); } - + CvtHex(HA1, a1); - + /* calculate H(A2) */ - MD5_Init(&Md5Ctx); - MD5_Update(&Md5Ctx, (unsigned char *)m, strlen(m)); - MD5_Update(&Md5Ctx, (unsigned char *)":", 1); - MD5_Update(&Md5Ctx, (unsigned char *)uri, strlen(uri)); + li_MD5_Init(&Md5Ctx); + li_MD5_Update(&Md5Ctx, (unsigned char *)m, strlen(m)); + li_MD5_Update(&Md5Ctx, (unsigned char *)":", 1); + li_MD5_Update(&Md5Ctx, (unsigned char *)uri, strlen(uri)); if (qop && strcasecmp(qop, "auth-int") == 0) { - MD5_Update(&Md5Ctx, (unsigned char *)":", 1); - MD5_Update(&Md5Ctx, (unsigned char *)"", HASHHEXLEN); + li_MD5_Update(&Md5Ctx, (unsigned char *)":", 1); + li_MD5_Update(&Md5Ctx, (unsigned char *)"", HASHHEXLEN); } - MD5_Final(HA2, &Md5Ctx); + li_MD5_Final(HA2, &Md5Ctx); CvtHex(HA2, HA2Hex); - + /* calculate response */ - MD5_Init(&Md5Ctx); - MD5_Update(&Md5Ctx, (unsigned char *)a1, HASHHEXLEN); - MD5_Update(&Md5Ctx, (unsigned char *)":", 1); - MD5_Update(&Md5Ctx, (unsigned char *)nonce, strlen(nonce)); - MD5_Update(&Md5Ctx, (unsigned char *)":", 1); + li_MD5_Init(&Md5Ctx); + li_MD5_Update(&Md5Ctx, (unsigned char *)a1, HASHHEXLEN); + li_MD5_Update(&Md5Ctx, (unsigned char *)":", 1); + li_MD5_Update(&Md5Ctx, (unsigned char *)nonce, strlen(nonce)); + li_MD5_Update(&Md5Ctx, (unsigned char *)":", 1); if (qop && *qop) { - MD5_Update(&Md5Ctx, (unsigned char *)nc, strlen(nc)); - MD5_Update(&Md5Ctx, (unsigned char *)":", 1); - MD5_Update(&Md5Ctx, (unsigned char *)cnonce, strlen(cnonce)); - MD5_Update(&Md5Ctx, (unsigned char *)":", 1); - MD5_Update(&Md5Ctx, (unsigned char *)qop, strlen(qop)); - MD5_Update(&Md5Ctx, (unsigned char *)":", 1); + li_MD5_Update(&Md5Ctx, (unsigned char *)nc, strlen(nc)); + li_MD5_Update(&Md5Ctx, (unsigned char *)":", 1); + li_MD5_Update(&Md5Ctx, (unsigned char *)cnonce, strlen(cnonce)); + li_MD5_Update(&Md5Ctx, (unsigned char *)":", 1); + li_MD5_Update(&Md5Ctx, (unsigned char *)qop, strlen(qop)); + li_MD5_Update(&Md5Ctx, (unsigned char *)":", 1); }; - MD5_Update(&Md5Ctx, (unsigned char *)HA2Hex, HASHHEXLEN); - MD5_Final(RespHash, &Md5Ctx); + li_MD5_Update(&Md5Ctx, (unsigned char *)HA2Hex, HASHHEXLEN); + li_MD5_Final(RespHash, &Md5Ctx); CvtHex(RespHash, a2); - + if (0 != strcmp(a2, respons)) { /* digest not ok */ - + if (p->conf.auth_debug) { - log_error_write(srv, __FILE__, __LINE__, "sss", + log_error_write(srv, __FILE__, __LINE__, "sss", "digest: digest mismatch", a2, respons); } - - log_error_write(srv, __FILE__, __LINE__, "sss", - "digest: auth failed for", username, "wrong password"); - + + log_error_write(srv, __FILE__, __LINE__, "ssss", + "digest: auth failed for ", username, ": wrong password, IP:", inet_ntop_cache_get_ip(srv, &(con->dst_addr))); + buffer_free(b); return 0; } - + /* value is our allow-rules */ if (http_auth_match_rules(srv, p, url->ptr, username, NULL, NULL)) { buffer_free(b); - - log_error_write(srv, __FILE__, __LINE__, "s", + + log_error_write(srv, __FILE__, __LINE__, "s", "digest: rules did match"); - + return 0; } - + /* remember the username */ buffer_copy_string(p->auth_user, username); - + buffer_free(b); - + if (p->conf.auth_debug) { - log_error_write(srv, __FILE__, __LINE__, "s", + log_error_write(srv, __FILE__, __LINE__, "s", "digest: auth ok"); } return 1; @@ -979,25 +1177,26 @@ int http_auth_digest_check(server *srv, connection *con, mod_auth_plugin_data *p int http_auth_digest_generate_nonce(server *srv, mod_auth_plugin_data *p, buffer *fn, char out[33]) { HASH h; - MD5_CTX Md5Ctx; + li_MD5_CTX Md5Ctx; char hh[32]; - + UNUSED(p); /* generate shared-secret */ - MD5_Init(&Md5Ctx); - MD5_Update(&Md5Ctx, (unsigned char *)fn->ptr, fn->used - 1); - MD5_Update(&Md5Ctx, (unsigned char *)"+", 1); - + li_MD5_Init(&Md5Ctx); + li_MD5_Update(&Md5Ctx, (unsigned char *)fn->ptr, fn->used - 1); + li_MD5_Update(&Md5Ctx, (unsigned char *)"+", 1); + /* we assume sizeof(time_t) == 4 here, but if not it ain't a problem at all */ - ltostr(hh, srv->cur_ts); - MD5_Update(&Md5Ctx, (unsigned char *)hh, strlen(hh)); - ltostr(hh, rand()); - MD5_Update(&Md5Ctx, (unsigned char *)hh, strlen(hh)); - - MD5_Final(h, &Md5Ctx); - + LI_ltostr(hh, srv->cur_ts); + li_MD5_Update(&Md5Ctx, (unsigned char *)hh, strlen(hh)); + li_MD5_Update(&Md5Ctx, (unsigned char *)srv->entropy, sizeof(srv->entropy)); + LI_ltostr(hh, rand()); + li_MD5_Update(&Md5Ctx, (unsigned char *)hh, strlen(hh)); + + li_MD5_Final(h, &Md5Ctx); + CvtHex(h, out); - + return 0; } diff --git a/src/http_auth.h b/src/http_auth.h index 0b664fa..5828a7e 100644 --- a/src/http_auth.h +++ b/src/http_auth.h @@ -9,22 +9,26 @@ # include <ldap.h> #endif -typedef enum { AUTH_BACKEND_UNSET, AUTH_BACKEND_PLAIN, - AUTH_BACKEND_LDAP, AUTH_BACKEND_HTPASSWD, - AUTH_BACKEND_HTDIGEST, AUTH_BACKEND_PAM } auth_backend_t; +typedef enum { + AUTH_BACKEND_UNSET, + AUTH_BACKEND_PLAIN, + AUTH_BACKEND_LDAP, + AUTH_BACKEND_HTPASSWD, + AUTH_BACKEND_HTDIGEST +} auth_backend_t; typedef struct { /* auth */ array *auth_require; - + buffer *auth_plain_groupfile; buffer *auth_plain_userfile; - + buffer *auth_htdigest_userfile; buffer *auth_htpasswd_userfile; - + buffer *auth_backend_conf; - + buffer *auth_ldap_hostname; buffer *auth_ldap_basedn; buffer *auth_ldap_binddn; @@ -32,15 +36,16 @@ typedef struct { buffer *auth_ldap_filter; buffer *auth_ldap_cafile; unsigned short auth_ldap_starttls; - + unsigned short auth_ldap_allow_empty_pw; + unsigned short auth_debug; - + /* generated */ auth_backend_t auth_backend; - + #ifdef USE_LDAP LDAP *ldap; - + buffer *ldap_filter_pre; buffer *ldap_filter_post; #endif @@ -49,16 +54,16 @@ typedef struct { typedef struct { PLUGIN_DATA; buffer *tmp_buf; - + buffer *auth_user; #ifdef USE_LDAP buffer *ldap_filter; #endif - + mod_auth_plugin_config **config_storage; - - mod_auth_plugin_config conf; /* this is only used as long as no handler_ctx is setup */ + + mod_auth_plugin_config conf, *anon_conf; /* this is only used as long as no handler_ctx is setup */ } mod_auth_plugin_data; int http_auth_basic_check(server *srv, connection *con, mod_auth_plugin_data *p, array *req, buffer *url, const char *realm_str); diff --git a/src/http_auth_digest.c b/src/http_auth_digest.c deleted file mode 100644 index e440430..0000000 --- a/src/http_auth_digest.c +++ /dev/null @@ -1,19 +0,0 @@ -#include <string.h> -#include "http_auth_digest.h" - -#include "buffer.h" - -#ifndef USE_OPENSSL -# include "md5.h" -#endif - -void CvtHex(IN HASH Bin, OUT HASHHEX Hex) { - unsigned short i; - - for (i = 0; i < HASHLEN; i++) { - Hex[i*2] = int2hex((Bin[i] >> 4) & 0xf); - Hex[i*2+1] = int2hex(Bin[i] & 0xf); - } - Hex[HASHHEXLEN] = '\0'; -} - diff --git a/src/http_auth_digest.h b/src/http_auth_digest.h deleted file mode 100644 index 8bffce4..0000000 --- a/src/http_auth_digest.h +++ /dev/null @@ -1,24 +0,0 @@ -#ifndef _DIGCALC_H_ -#define _DIGCALC_H_ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#define HASHLEN 16 -typedef unsigned char HASH[HASHLEN]; -#define HASHHEXLEN 32 -typedef char HASHHEX[HASHHEXLEN+1]; -#ifdef USE_OPENSSL -#define IN const -#else -#define IN -#endif -#define OUT - -void CvtHex( - IN HASH Bin, - OUT HASHHEX Hex - ); - -#endif diff --git a/src/http_chunk.c b/src/http_chunk.c index c128bf1..5557edc 100644 --- a/src/http_chunk.c +++ b/src/http_chunk.c @@ -1,9 +1,14 @@ /** * the HTTP chunk-API - * - * + * + * */ +#include "server.h" +#include "chunk.h" +#include "http_chunk.h" +#include "log.h" + #include <sys/types.h> #include <sys/stat.h> @@ -15,27 +20,22 @@ #include <errno.h> #include <string.h> -#include "server.h" -#include "chunk.h" -#include "http_chunk.h" -#include "log.h" - static int http_chunk_append_len(server *srv, connection *con, size_t len) { size_t i, olen = len, j; buffer *b; - + b = srv->tmp_chunk_len; - + if (len == 0) { - buffer_copy_string(b, "0"); + buffer_copy_string_len(b, CONST_STR_LEN("0")); } else { for (i = 0; i < 8 && len; i++) { len >>= 4; } - + /* i is the number of hex digits we have */ buffer_prepare_copy(b, i + 1); - + for (j = i-1, len = olen; j+1 > 0; j--) { b->ptr[j] = (len & 0xf) + (((len & 0xf) <= 9) ? '0' : 'a' - 10); len >>= 4; @@ -43,81 +43,80 @@ static int http_chunk_append_len(server *srv, connection *con, size_t len) { b->used = i; b->ptr[b->used++] = '\0'; } - - buffer_append_string(b, "\r\n"); + + buffer_append_string_len(b, CONST_STR_LEN("\r\n")); chunkqueue_append_buffer(con->write_queue, b); - + return 0; } int http_chunk_append_file(server *srv, connection *con, buffer *fn, off_t offset, off_t len) { chunkqueue *cq; - + if (!con) return -1; - + cq = con->write_queue; - + if (con->response.transfer_encoding & HTTP_TRANSFER_ENCODING_CHUNKED) { http_chunk_append_len(srv, con, len); } - + chunkqueue_append_file(cq, fn, offset, len); - + if (con->response.transfer_encoding & HTTP_TRANSFER_ENCODING_CHUNKED && len > 0) { chunkqueue_append_mem(cq, "\r\n", 2 + 1); } - + return 0; } int http_chunk_append_buffer(server *srv, connection *con, buffer *mem) { chunkqueue *cq; - + if (!con) return -1; - + cq = con->write_queue; - + if (con->response.transfer_encoding & HTTP_TRANSFER_ENCODING_CHUNKED) { http_chunk_append_len(srv, con, mem->used - 1); } - + chunkqueue_append_buffer(cq, mem); - + if (con->response.transfer_encoding & HTTP_TRANSFER_ENCODING_CHUNKED && mem->used > 0) { chunkqueue_append_mem(cq, "\r\n", 2 + 1); } - + return 0; } int http_chunk_append_mem(server *srv, connection *con, const char * mem, size_t len) { chunkqueue *cq; - + if (!con) return -1; - + cq = con->write_queue; - + if (len == 0) { if (con->response.transfer_encoding & HTTP_TRANSFER_ENCODING_CHUNKED) { - http_chunk_append_len(srv, con, 0); - chunkqueue_append_mem(cq, "\r\n", 2 + 1); + chunkqueue_append_mem(cq, "0\r\n\r\n", 5 + 1); } else { chunkqueue_append_mem(cq, "", 1); } return 0; } - + if (con->response.transfer_encoding & HTTP_TRANSFER_ENCODING_CHUNKED) { http_chunk_append_len(srv, con, len - 1); } - + chunkqueue_append_mem(cq, mem, len); - + if (con->response.transfer_encoding & HTTP_TRANSFER_ENCODING_CHUNKED) { chunkqueue_append_mem(cq, "\r\n", 2 + 1); } - + return 0; } @@ -125,9 +124,9 @@ int http_chunk_append_mem(server *srv, connection *con, const char * mem, size_t off_t http_chunkqueue_length(server *srv, connection *con) { if (!con) { log_error_write(srv, __FILE__, __LINE__, "s", "connection is NULL!!"); - + return 0; } - + return chunkqueue_length(con->write_queue); } diff --git a/src/inet_ntop_cache.c b/src/inet_ntop_cache.c index c0b3aa1..eac0681 100644 --- a/src/inet_ntop_cache.c +++ b/src/inet_ntop_cache.c @@ -1,17 +1,16 @@ -#include <sys/types.h> - -#include <string.h> - - #include "base.h" #include "inet_ntop_cache.h" #include "sys-socket.h" +#include <sys/types.h> + +#include <string.h> + const char * inet_ntop_cache_get_ip(server *srv, sock_addr *addr) { -#ifdef HAVE_IPV6 +#ifdef HAVE_IPV6 size_t ndx = 0, i; for (i = 0; i < INET_NTOP_CACHE_MAX; i++) { - if (srv->inet_ntop_cache[i].ts != 0) { + if (srv->inet_ntop_cache[i].ts != 0 && srv->inet_ntop_cache[i].family == addr->plain.sa_family) { if (srv->inet_ntop_cache[i].family == AF_INET6 && 0 == memcmp(srv->inet_ntop_cache[i].addr.ipv6.s6_addr, addr->ipv6.sin6_addr.s6_addr, 16)) { /* IPv6 found in cache */ @@ -20,31 +19,31 @@ const char * inet_ntop_cache_get_ip(server *srv, sock_addr *addr) { srv->inet_ntop_cache[i].addr.ipv4.s_addr == addr->ipv4.sin_addr.s_addr) { /* IPv4 found in cache */ break; - + } } } - + if (i == INET_NTOP_CACHE_MAX) { /* not found in cache */ - + i = ndx; - inet_ntop(addr->plain.sa_family, - addr->plain.sa_family == AF_INET6 ? + inet_ntop(addr->plain.sa_family, + addr->plain.sa_family == AF_INET6 ? (const void *) &(addr->ipv6.sin6_addr) : (const void *) &(addr->ipv4.sin_addr), srv->inet_ntop_cache[i].b2, INET6_ADDRSTRLEN); - + srv->inet_ntop_cache[i].ts = srv->cur_ts; srv->inet_ntop_cache[i].family = addr->plain.sa_family; - + if (srv->inet_ntop_cache[i].family == AF_INET) { srv->inet_ntop_cache[i].addr.ipv4.s_addr = addr->ipv4.sin_addr.s_addr; } else if (srv->inet_ntop_cache[i].family == AF_INET6) { memcpy(srv->inet_ntop_cache[i].addr.ipv6.s6_addr, addr->ipv6.sin6_addr.s6_addr, 16); } } - + return srv->inet_ntop_cache[i].b2; #else UNUSED(srv); diff --git a/src/joblist.c b/src/joblist.c index dcab955..677a1a5 100644 --- a/src/joblist.c +++ b/src/joblist.c @@ -1,13 +1,13 @@ -#include <stdlib.h> -#include <string.h> - #include "base.h" #include "joblist.h" #include "log.h" +#include <stdlib.h> +#include <string.h> + int joblist_append(server *srv, connection *con) { if (con->in_joblist) return 0; - + if (srv->joblist->size == 0) { srv->joblist->size = 16; srv->joblist->ptr = malloc(sizeof(*srv->joblist->ptr) * srv->joblist->size); @@ -15,15 +15,15 @@ int joblist_append(server *srv, connection *con) { srv->joblist->size += 16; srv->joblist->ptr = realloc(srv->joblist->ptr, sizeof(*srv->joblist->ptr) * srv->joblist->size); } - + srv->joblist->ptr[srv->joblist->used++] = con; - + return 0; } void joblist_free(server *srv, connections *joblist) { UNUSED(srv); - + free(joblist->ptr); free(joblist); } @@ -31,14 +31,14 @@ void joblist_free(server *srv, connections *joblist) { connection *fdwaitqueue_unshift(server *srv, connections *fdwaitqueue) { connection *con; UNUSED(srv); - - + + if (fdwaitqueue->used == 0) return NULL; - + con = fdwaitqueue->ptr[0]; - + memmove(fdwaitqueue->ptr, &(fdwaitqueue->ptr[1]), --fdwaitqueue->used * sizeof(*(fdwaitqueue->ptr))); - + return con; } @@ -50,9 +50,9 @@ int fdwaitqueue_append(server *srv, connection *con) { srv->fdwaitqueue->size += 16; srv->fdwaitqueue->ptr = realloc(srv->fdwaitqueue->ptr, sizeof(*(srv->fdwaitqueue->ptr)) * srv->fdwaitqueue->size); } - + srv->fdwaitqueue->ptr[srv->fdwaitqueue->used++] = con; - + return 0; } diff --git a/src/keyvalue.c b/src/keyvalue.c index b26588f..ba37611 100644 --- a/src/keyvalue.c +++ b/src/keyvalue.c @@ -1,10 +1,11 @@ +#include "server.h" +#include "keyvalue.h" +#include "log.h" + #include <stdlib.h> #include <string.h> #include <stdio.h> -#include "server.h" -#include "keyvalue.h" - static keyvalue http_versions[] = { { HTTP_VERSION_1_1, "HTTP/1.1" }, { HTTP_VERSION_1_0, "HTTP/1.0" }, @@ -21,12 +22,17 @@ static keyvalue http_methods[] = { { HTTP_METHOD_OPTIONS, "OPTIONS" }, { HTTP_METHOD_MKCOL, "MKCOL" }, { HTTP_METHOD_PUT, "PUT" }, + { HTTP_METHOD_PATCH, "PATCH" }, { HTTP_METHOD_DELETE, "DELETE" }, { HTTP_METHOD_COPY, "COPY" }, { HTTP_METHOD_MOVE, "MOVE" }, { HTTP_METHOD_LABEL, "LABEL" }, { HTTP_METHOD_CHECKOUT, "CHECKOUT" }, { HTTP_METHOD_CHECKIN, "CHECKIN" }, + { HTTP_METHOD_MERGE, "MERGE" }, + { HTTP_METHOD_LOCK, "LOCK" }, + { HTTP_METHOD_UNLOCK, "UNLOCK" }, + { HTTP_METHOD_MKACTIVITY, "MKACTIVITY" }, { HTTP_METHOD_UNCHECKOUT, "UNCHECKOUT" }, { HTTP_METHOD_VERSION_CONTROL, "VERSION-CONTROL" }, { HTTP_METHOD_CONNECT, "CONNECT" }, @@ -83,7 +89,7 @@ static keyvalue http_status[] = { { 504, "Gateway Timeout" }, { 505, "HTTP Version Not Supported" }, { 507, "Insufficient Storage" }, /* WebDAV */ - + { -1, NULL } }; @@ -98,12 +104,12 @@ static keyvalue http_status_body[] = { { 501, "501.html" }, { 503, "503.html" }, { 505, "505.html" }, - + { -1, NULL } }; -const char *keyvalue_get_value(keyvalue *kv, int k) { +const char *keyvalue_get_value(keyvalue *kv, int k) { int i; for (i = 0; kv[i].value; i++) { if (kv[i].key == k) return kv[i].value; @@ -111,7 +117,7 @@ const char *keyvalue_get_value(keyvalue *kv, int k) { return NULL; } -int keyvalue_get_key(keyvalue *kv, const char *s) { +int keyvalue_get_key(keyvalue *kv, const char *s) { int i; for (i = 0; kv[i].value; i++) { if (0 == strcmp(kv[i].value, s)) return kv[i].key; @@ -121,9 +127,9 @@ int keyvalue_get_key(keyvalue *kv, const char *s) { keyvalue_buffer *keyvalue_buffer_init(void) { keyvalue_buffer *kvb; - + kvb = calloc(1, sizeof(*kvb)); - + return kvb; } @@ -131,49 +137,49 @@ int keyvalue_buffer_append(keyvalue_buffer *kvb, int key, const char *value) { size_t i; if (kvb->size == 0) { kvb->size = 4; - + kvb->kv = malloc(kvb->size * sizeof(*kvb->kv)); - + for(i = 0; i < kvb->size; i++) { kvb->kv[i] = calloc(1, sizeof(**kvb->kv)); } } else if (kvb->used == kvb->size) { kvb->size += 4; - + kvb->kv = realloc(kvb->kv, kvb->size * sizeof(*kvb->kv)); - + for(i = kvb->used; i < kvb->size; i++) { kvb->kv[i] = calloc(1, sizeof(**kvb->kv)); } } - + kvb->kv[kvb->used]->key = key; kvb->kv[kvb->used]->value = strdup(value); - + kvb->used++; - + return 0; } void keyvalue_buffer_free(keyvalue_buffer *kvb) { size_t i; - + for (i = 0; i < kvb->size; i++) { if (kvb->kv[i]->value) free(kvb->kv[i]->value); free(kvb->kv[i]); } - + if (kvb->kv) free(kvb->kv); - + free(kvb); } s_keyvalue_buffer *s_keyvalue_buffer_init(void) { s_keyvalue_buffer *kvb; - + kvb = calloc(1, sizeof(*kvb)); - + return kvb; } @@ -182,50 +188,50 @@ int s_keyvalue_buffer_append(s_keyvalue_buffer *kvb, const char *key, const char if (kvb->size == 0) { kvb->size = 4; kvb->used = 0; - + kvb->kv = malloc(kvb->size * sizeof(*kvb->kv)); - + for(i = 0; i < kvb->size; i++) { kvb->kv[i] = calloc(1, sizeof(**kvb->kv)); } } else if (kvb->used == kvb->size) { kvb->size += 4; - + kvb->kv = realloc(kvb->kv, kvb->size * sizeof(*kvb->kv)); - + for(i = kvb->used; i < kvb->size; i++) { kvb->kv[i] = calloc(1, sizeof(**kvb->kv)); } } - + kvb->kv[kvb->used]->key = key ? strdup(key) : NULL; kvb->kv[kvb->used]->value = strdup(value); - + kvb->used++; - + return 0; } void s_keyvalue_buffer_free(s_keyvalue_buffer *kvb) { size_t i; - + for (i = 0; i < kvb->size; i++) { if (kvb->kv[i]->key) free(kvb->kv[i]->key); if (kvb->kv[i]->value) free(kvb->kv[i]->value); free(kvb->kv[i]); } - + if (kvb->kv) free(kvb->kv); - + free(kvb); } httpauth_keyvalue_buffer *httpauth_keyvalue_buffer_init(void) { httpauth_keyvalue_buffer *kvb; - + kvb = calloc(1, sizeof(*kvb)); - + return kvb; } @@ -233,42 +239,42 @@ int httpauth_keyvalue_buffer_append(httpauth_keyvalue_buffer *kvb, const char *k size_t i; if (kvb->size == 0) { kvb->size = 4; - + kvb->kv = malloc(kvb->size * sizeof(*kvb->kv)); - + for(i = 0; i < kvb->size; i++) { kvb->kv[i] = calloc(1, sizeof(**kvb->kv)); } } else if (kvb->used == kvb->size) { kvb->size += 4; - + kvb->kv = realloc(kvb->kv, kvb->size * sizeof(*kvb->kv)); - + for(i = kvb->used; i < kvb->size; i++) { kvb->kv[i] = calloc(1, sizeof(**kvb->kv)); } } - + kvb->kv[kvb->used]->key = strdup(key); kvb->kv[kvb->used]->realm = strdup(realm); kvb->kv[kvb->used]->type = type; - + kvb->used++; - + return 0; } void httpauth_keyvalue_buffer_free(httpauth_keyvalue_buffer *kvb) { size_t i; - + for (i = 0; i < kvb->size; i++) { if (kvb->kv[i]->key) free(kvb->kv[i]->key); if (kvb->kv[i]->realm) free(kvb->kv[i]->realm); free(kvb->kv[i]); } - + if (kvb->kv) free(kvb->kv); - + free(kvb); } @@ -302,59 +308,60 @@ http_method_t get_http_method_key(const char *s) { pcre_keyvalue_buffer *pcre_keyvalue_buffer_init(void) { pcre_keyvalue_buffer *kvb; - + kvb = calloc(1, sizeof(*kvb)); - + return kvb; } -int pcre_keyvalue_buffer_append(pcre_keyvalue_buffer *kvb, const char *key, const char *value) { +int pcre_keyvalue_buffer_append(server *srv, pcre_keyvalue_buffer *kvb, const char *key, const char *value) { #ifdef HAVE_PCRE_H size_t i; const char *errptr; int erroff; pcre_keyvalue *kv; #endif - + if (!key) return -1; #ifdef HAVE_PCRE_H if (kvb->size == 0) { kvb->size = 4; kvb->used = 0; - + kvb->kv = malloc(kvb->size * sizeof(*kvb->kv)); - + for(i = 0; i < kvb->size; i++) { kvb->kv[i] = calloc(1, sizeof(**kvb->kv)); } } else if (kvb->used == kvb->size) { kvb->size += 4; - + kvb->kv = realloc(kvb->kv, kvb->size * sizeof(*kvb->kv)); - + for(i = kvb->used; i < kvb->size; i++) { kvb->kv[i] = calloc(1, sizeof(**kvb->kv)); } } - + kv = kvb->kv[kvb->used]; if (NULL == (kv->key = pcre_compile(key, 0, &errptr, &erroff, NULL))) { - - fprintf(stderr, "%s.%d: rexexp compilation error at %s\n", __FILE__, __LINE__, errptr); + + log_error_write(srv, __FILE__, __LINE__, "SS", + "rexexp compilation error at ", errptr); return -1; } - if (NULL == (kv->key_extra = pcre_study(kv->key, 0, &errptr)) && + if (NULL == (kv->key_extra = pcre_study(kv->key, 0, &errptr)) && errptr != NULL) { return -1; } - + kv->value = buffer_init_string(value); - + kvb->used++; - + return 0; #else UNUSED(kvb); @@ -376,9 +383,9 @@ void pcre_keyvalue_buffer_free(pcre_keyvalue_buffer *kvb) { if (kv->value) buffer_free(kv->value); free(kv); } - + if (kvb->kv) free(kvb->kv); #endif - + free(kvb); } diff --git a/src/keyvalue.h b/src/keyvalue.h index e1c940f..43ef99b 100644 --- a/src/keyvalue.h +++ b/src/keyvalue.h @@ -2,31 +2,38 @@ #define _KEY_VALUE_H_ #ifdef HAVE_CONFIG_H -#include "config.h" +# include "config.h" #endif #ifdef HAVE_PCRE_H # include <pcre.h> #endif -typedef enum { - HTTP_METHOD_UNSET = -1, - HTTP_METHOD_GET, - HTTP_METHOD_POST, - HTTP_METHOD_HEAD, - HTTP_METHOD_OPTIONS, +struct server; + +typedef enum { + HTTP_METHOD_UNSET = -1, + HTTP_METHOD_GET, + HTTP_METHOD_POST, + HTTP_METHOD_HEAD, + HTTP_METHOD_OPTIONS, HTTP_METHOD_PROPFIND, /* WebDAV */ - HTTP_METHOD_MKCOL, - HTTP_METHOD_PUT, - HTTP_METHOD_DELETE, - HTTP_METHOD_COPY, - HTTP_METHOD_MOVE, - HTTP_METHOD_PROPPATCH, + HTTP_METHOD_MKCOL, + HTTP_METHOD_PUT, + HTTP_METHOD_PATCH, + HTTP_METHOD_DELETE, + HTTP_METHOD_COPY, + HTTP_METHOD_MOVE, + HTTP_METHOD_PROPPATCH, HTTP_METHOD_REPORT, /* DeltaV */ HTTP_METHOD_CHECKOUT, HTTP_METHOD_CHECKIN, HTTP_METHOD_VERSION_CONTROL, HTTP_METHOD_UNCHECKOUT, + HTTP_METHOD_MKACTIVITY, + HTTP_METHOD_MERGE, + HTTP_METHOD_LOCK, + HTTP_METHOD_UNLOCK, HTTP_METHOD_LABEL, HTTP_METHOD_CONNECT } http_method_t; @@ -35,13 +42,13 @@ typedef enum { HTTP_VERSION_UNSET = -1, HTTP_VERSION_1_0, HTTP_VERSION_1_1 } htt typedef struct { int key; - + char *value; } keyvalue; typedef struct { char *key; - + char *value; } s_keyvalue; @@ -50,7 +57,7 @@ typedef struct { pcre *key; pcre_extra *key_extra; #endif - + buffer *value; } pcre_keyvalue; @@ -58,7 +65,7 @@ typedef enum { HTTP_AUTH_BASIC, HTTP_AUTH_DIGEST } httpauth_type; typedef struct { char *key; - + char *realm; httpauth_type type; } httpauth_keyvalue; @@ -98,7 +105,7 @@ int httpauth_keyvalue_buffer_append(httpauth_keyvalue_buffer *kvb, const char *k void httpauth_keyvalue_buffer_free(httpauth_keyvalue_buffer *kvb); pcre_keyvalue_buffer *pcre_keyvalue_buffer_init(void); -int pcre_keyvalue_buffer_append(pcre_keyvalue_buffer *kvb, const char *key, const char *value); +int pcre_keyvalue_buffer_append(struct server *srv, pcre_keyvalue_buffer *kvb, const char *key, const char *value); void pcre_keyvalue_buffer_free(pcre_keyvalue_buffer *kvb); #endif diff --git a/src/lemon.c b/src/lemon.c index dd87cdf..c3d72eb 100644 --- a/src/lemon.c +++ b/src/lemon.c @@ -12,12 +12,26 @@ #include <ctype.h> #include <stdlib.h> +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_STDINT_H +# include <stdint.h> +#endif +#ifdef HAVE_INTTYPES_H +# include <inttypes.h> +#endif + +#define UNUSED(x) ( (void)(x) ) + extern void qsort(); extern double strtod(); extern long strtol(); extern void free(); extern int access(); extern int atoi(); +extern char *getenv(); #ifndef __WIN32__ # if defined(_WIN32) || defined(WIN32) @@ -25,8 +39,14 @@ extern int atoi(); # endif #endif +#if __GNUC__ > 2 +#define NORETURN __attribute__ ((__noreturn__)) +#else +#define NORETURN +#endif + /* #define PRIVATE static */ -#define PRIVATE +#define PRIVATE static #ifdef TEST #define MAXRHS 5 /* Set low to exercise exception code */ @@ -37,13 +57,15 @@ extern int atoi(); char *msort(); extern void *malloc(); +extern void memory_error() NORETURN; + /******** From the file "action.h" *************************************/ struct action *Action_new(); struct action *Action_sort(); void Action_add(); /********* From the file "assert.h" ************************************/ -void myassert(); +void myassert() NORETURN; #ifndef NDEBUG # define assert(X) if(!(X))myassert(__FILE__,__LINE__) #else @@ -278,7 +300,6 @@ struct lemon { }; #define MemoryCheck(X) if((X)==0){ \ - extern void memory_error(); \ memory_error(); \ } @@ -336,10 +357,10 @@ void Configtable_clear(/* int(*)(struct config *) */); /* Allocate a new parser action */ struct action *Action_new(){ - static struct action *freelist = 0; + static struct action *freelist = NULL; struct action *new; - if( freelist==0 ){ + if( freelist==NULL ){ int i; int amt = 100; freelist = (struct action *)malloc( sizeof(struct action)*amt ); @@ -383,7 +404,7 @@ void Action_add(app,type,sp,arg) struct action **app; enum e_action type; struct symbol *sp; -char *arg; +void *arg; { struct action *new; new = Action_new(); @@ -432,14 +453,16 @@ struct acttab { #define acttab_yylookahead(X,N) ((X)->aAction[N].lookahead) /* Free all memory associated with the given acttab */ -void acttab_free(acttab *p){ +/* +PRIVATE void acttab_free(acttab *p){ free( p->aAction ); free( p->aLookahead ); free( p ); } +*/ /* Allocate a new acttab structure */ -acttab *acttab_alloc(void){ +PRIVATE acttab *acttab_alloc(void){ acttab *p = malloc( sizeof(*p) ); if( p==0 ){ fprintf(stderr,"Unable to allocate memory for a new acttab."); @@ -451,7 +474,7 @@ acttab *acttab_alloc(void){ /* Add a new action to the current transaction set */ -void acttab_action(acttab *p, int lookahead, int action){ +PRIVATE void acttab_action(acttab *p, int lookahead, int action){ if( p->nLookahead>=p->nLookaheadAlloc ){ p->nLookaheadAlloc += 25; p->aLookahead = realloc( p->aLookahead, @@ -484,7 +507,7 @@ void acttab_action(acttab *p, int lookahead, int action){ ** ** Return the offset into the action table of the new transaction. */ -int acttab_insert(acttab *p){ +PRIVATE int acttab_insert(acttab *p){ int i, j, k, n; assert( p->nLookahead>0 ); @@ -579,7 +602,7 @@ int line; */ /* Find a precedence symbol of every rule in the grammar. -** +** ** Those rules which have a precedence symbol coded in the input ** grammar using the "[symbol]" construct will already have the ** rp->precsym field filled. Other rules take as their precedence @@ -869,7 +892,7 @@ struct lemon *lemp; cfp->status = INCOMPLETE; } } - + do{ progress = 0; for(i=0; i<lemp->nstate; i++){ @@ -900,7 +923,7 @@ struct lemon *lemp; struct symbol *sp; struct rule *rp; - /* Add all of the reduce actions + /* Add all of the reduce actions ** A reduce action is added for each element of the followset of ** a configuration which has its dot at the extreme right. */ @@ -983,6 +1006,7 @@ struct symbol *errsym; /* The error symbol (if defined. NULL otherwise) */ { struct symbol *spx, *spy; int errcnt = 0; + UNUSED(errsym); assert( apx->sp==apy->sp ); /* Otherwise there would be no conflict */ if( apx->type==SHIFT && apy->type==REDUCE ){ spx = apx->sp; @@ -1017,7 +1041,7 @@ struct symbol *errsym; /* The error symbol (if defined. NULL otherwise) */ apx->type = RD_RESOLVED; } }else{ - assert( + assert( apx->type==SH_RESOLVED || apx->type==RD_RESOLVED || apx->type==CONFLICT || @@ -1315,7 +1339,7 @@ void ErrorMsg(const char *filename, int lineno, const char *format, ...){ /* Report an out-of-memory condition and abort. This function ** is used mostly by the "MemoryCheck" macro in struct.h */ -void memory_error(){ +void memory_error() { fprintf(stderr,"Out of memory. Aborting...\n"); exit(1); } @@ -1347,10 +1371,11 @@ char **argv; struct lemon lem; char *def_tmpl_name = "lempar.c"; + UNUSED(argc); OptInit(argv,options,stderr); if( version ){ printf("Lemon version 1.0\n"); - exit(0); + exit(0); } if( OptNArgs() < 1 ){ fprintf(stderr,"Exactly one filename argument is required.\n"); @@ -1589,7 +1614,6 @@ int k; FILE *err; { int spcnt, i; - spcnt = 0; if( argv[0] ) fprintf(err,"%s",argv[0]); spcnt = strlen(argv[0]) + 1; for(i=1; i<n && argv[i]; i++){ @@ -1651,7 +1675,7 @@ FILE *err; }else if( op[j].type==OPT_FLAG ){ *((int*)op[j].arg) = v; }else if( op[j].type==OPT_FFLAG ){ - (*(void(*)())(op[j].arg))(v); + (*(void(*)())(intptr_t)(op[j].arg))(v); }else{ if( err ){ fprintf(err,"%smissing argument on switch.\n",emsg); @@ -1733,19 +1757,19 @@ FILE *err; *(double*)(op[j].arg) = dv; break; case OPT_FDBL: - (*(void(*)())(op[j].arg))(dv); + (*(void(*)())(intptr_t)(op[j].arg))(dv); break; case OPT_INT: *(int*)(op[j].arg) = lv; break; case OPT_FINT: - (*(void(*)())(op[j].arg))((int)lv); + (*(void(*)())(intptr_t)(op[j].arg))((int)lv); break; case OPT_STR: *(char**)(op[j].arg) = sv; break; case OPT_FSTR: - (*(void(*)())(op[j].arg))(sv); + (*(void(*)())(intptr_t)(op[j].arg))(sv); break; } } @@ -2031,7 +2055,7 @@ to follow the previous rule."); case IN_RHS: if( x[0]=='.' ){ struct rule *rp; - rp = (struct rule *)malloc( sizeof(struct rule) + + rp = (struct rule *)malloc( sizeof(struct rule) + sizeof(struct symbol*)*psp->nrhs + sizeof(char*)*psp->nrhs ); if( rp==0 ){ ErrorMsg(psp->filename,psp->tokenlineno, @@ -2286,10 +2310,10 @@ to follow the previous rule."); ** token is passed to the function "parseonetoken" which builds all ** the appropriate data structures in the global state vector "gp". */ +struct pstate ps; void Parse(gp) struct lemon *gp; { - struct pstate ps; FILE *fp; char *filebuf; size_t filesize; @@ -2317,6 +2341,7 @@ struct lemon *gp; if( filebuf==0 ){ ErrorMsg(ps.filename,0,"Can't allocate %d of memory to hold this file.", filesize+1); + fclose(fp); gp->errorcnt++; return; } @@ -2324,6 +2349,7 @@ struct lemon *gp; ErrorMsg(ps.filename,0,"Can't read in all %d bytes of this file.", filesize); free(filebuf); + fclose(fp); gp->errorcnt++; return; } @@ -2546,7 +2572,7 @@ char *mode; return fp; } -/* Duplicate the input file without comments and without actions +/* Duplicate the input file without comments and without actions ** on rules */ void Reprint(lemp) struct lemon *lemp; @@ -2588,7 +2614,7 @@ struct lemon *lemp; } } -void ConfigPrint(fp,cfp) +PRIVATE void ConfigPrint(fp,cfp) FILE *fp; struct config *cfp; { @@ -2625,7 +2651,7 @@ struct lemon *lemp; } /* Print a plink chain */ -PRIVATE void PlinkPrint(out,plp,tag) +void PlinkPrint(out,plp,tag) FILE *out; struct plink *plp; char *tag; @@ -2642,7 +2668,7 @@ char *tag; /* Print an action to the given file descriptor. Return FALSE if ** nothing was actually printed. */ -int PrintAction(struct action *ap, FILE *fp, int indent){ +PRIVATE int PrintAction(struct action *ap, FILE *fp, int indent){ int result = 1; switch( ap->type ){ case SHIFT: @@ -2716,6 +2742,7 @@ struct lemon *lemp; return; } + extern int access(); /* Search for the file "name" which is in the same directory as ** the exacutable */ PRIVATE char *pathsearch(argv0,name,modemask) @@ -2726,7 +2753,6 @@ int modemask; char *pathlist; char *path,*cp; char c; - extern int access(); #ifdef __WIN32__ cp = strrchr(argv0,'\\'); @@ -2740,7 +2766,6 @@ int modemask; if( path ) sprintf(path,"%s/%s",argv0,name); *cp = c; }else{ - extern char *getenv(); pathlist = getenv("PATH"); if( pathlist==0 ) pathlist = ".:/bin:/usr/bin"; path = (char *)malloc( strlen(pathlist)+strlen(name)+2 ); @@ -2822,7 +2847,7 @@ int *lineno; PRIVATE FILE *tplt_open(lemp) struct lemon *lemp; { - + char buf[1000]; FILE *in; char *tpltname; @@ -2879,7 +2904,7 @@ int *lineno; ** The following routine emits code for the destructor for the ** symbol sp */ -void emit_destructor_code(out,sp,lemp,lineno) +PRIVATE void emit_destructor_code(out,sp,lemp,lineno) FILE *out; struct symbol *sp; struct lemon *lemp; @@ -2895,7 +2920,7 @@ int *lineno; }else if( sp->destructor ){ cp = sp->destructor; fprintf(out,"#line %d \"%s\"\n{",sp->destructorln,lemp->filename); - }else if( lemp->vardest ){ + }else{ cp = lemp->vardest; if( cp==0 ) return; fprintf(out,"#line %d \"%s\"\n{",lemp->vardestln,lemp->filename); @@ -2917,7 +2942,7 @@ int *lineno; /* ** Return TRUE (non-zero) if the given symbol has a destructor. */ -int has_destructor(sp, lemp) +PRIVATE int has_destructor(sp, lemp) struct symbol *sp; struct lemon *lemp; { @@ -2930,7 +2955,7 @@ struct lemon *lemp; return ret; } -/* +/* ** Generate code which executes when the rule "rp" is reduced. Write ** the code to "out". Make sure lineno stays up-to-date. */ @@ -3018,13 +3043,13 @@ int *lineno; ** union, also set the ".dtnum" field of every terminal and nonterminal ** symbol. */ -void print_stack_union(out,lemp,plineno,mhflag) +PRIVATE void print_stack_union(out,lemp,plineno,mhflag) FILE *out; /* The output stream */ struct lemon *lemp; /* The main info structure for this parser */ int *plineno; /* Pointer to the line number */ int mhflag; /* True if generating makeheaders output */ { - int lineno = *plineno; /* The line number of the output */ + int lineno; /* The line number of the output */ char **types; /* A hash table of datatypes */ int arraysize; /* Size of the "types" array */ int maxdtlength; /* Maximum length of any ".datatype" field. */ @@ -3384,7 +3409,7 @@ int mhflag; /* Output in makeheaders format if true */ /* Output the yy_shift_ofst[] table */ fprintf(out, "#define YY_SHIFT_USE_DFLT (%d)\n", mnTknOfst-1); lineno++; - fprintf(out, "static %s yy_shift_ofst[] = {\n", + fprintf(out, "static %s yy_shift_ofst[] = {\n", minimum_size_type(mnTknOfst-1, mxTknOfst)); lineno++; n = lemp->nstate; for(i=j=0; i<n; i++){ @@ -3405,7 +3430,7 @@ int mhflag; /* Output in makeheaders format if true */ /* Output the yy_reduce_ofst[] table */ fprintf(out, "#define YY_REDUCE_USE_DFLT (%d)\n", mnNtOfst-1); lineno++; - fprintf(out, "static %s yy_reduce_ofst[] = {\n", + fprintf(out, "static %s yy_reduce_ofst[] = {\n", minimum_size_type(mnNtOfst-1, mxNtOfst)); lineno++; n = lemp->nstate; for(i=j=0; i<n; i++){ @@ -3480,7 +3505,7 @@ int mhflag; /* Output in makeheaders format if true */ tplt_xfer(lemp->name,in,out,&lineno); /* Generate code which executes every time a symbol is popped from - ** the stack while processing errors or while destroying the parser. + ** the stack while processing errors or while destroying the parser. ** (In other words, generate the %destructor actions) */ if( lemp->tokendest ){ @@ -3522,7 +3547,7 @@ int mhflag; /* Output in makeheaders format if true */ tplt_print(out,lemp,lemp->overflow,lemp->overflowln,&lineno); tplt_xfer(lemp->name,in,out,&lineno); - /* Generate the table of rule information + /* Generate the table of rule information ** ** Note: This code depends on the fact that rules are number ** sequentually beginning with 0. @@ -3589,7 +3614,7 @@ struct lemon *lemp; for(i=1; i<lemp->nterminal; i++){ fprintf(out,"#define %s%-30s %2d\n",prefix,lemp->symbols[i]->name,i); } - fclose(out); + fclose(out); } return; } @@ -3630,7 +3655,7 @@ struct lemon *lemp; rbest = rp; } } - + /* Do not make a default if the number of rules to default ** is not at least 2 */ if( nbest<2 ) continue; @@ -3669,7 +3694,6 @@ char *SetNew(){ int i; s = (char*)malloc( global_size ); if( s==0 ){ - extern void memory_error(); memory_error(); } for(i=0; i<global_size; i++) s[i] = 0; @@ -3781,7 +3805,7 @@ void Strsafe_init(){ if( x1a ){ x1a->size = 1024; x1a->count = 0; - x1a->tbl = (x1node*)malloc( + x1a->tbl = (x1node*)malloc( (sizeof(x1node) + sizeof(x1node*))*1024 ); if( x1a->tbl==0 ){ free(x1a); @@ -3943,7 +3967,7 @@ void Symbol_init(){ if( x2a ){ x2a->size = 128; x2a->count = 0; - x2a->tbl = (x2node*)malloc( + x2a->tbl = (x2node*)malloc( (sizeof(x2node) + sizeof(x2node*))*128 ); if( x2a->tbl==0 ){ free(x2a); @@ -4149,7 +4173,7 @@ void State_init(){ if( x3a ){ x3a->size = 128; x3a->count = 0; - x3a->tbl = (x3node*)malloc( + x3a->tbl = (x3node*)malloc( (sizeof(x3node) + sizeof(x3node*))*128 ); if( x3a->tbl==0 ){ free(x3a); @@ -4295,7 +4319,7 @@ void Configtable_init(){ if( x4a ){ x4a->size = 64; x4a->count = 0; - x4a->tbl = (x4node*)malloc( + x4a->tbl = (x4node*)malloc( (sizeof(x4node) + sizeof(x4node*))*64 ); if( x4a->tbl==0 ){ free(x4a); diff --git a/src/lempar.c b/src/lempar.c index ee1edbf..ef9bd94 100644 --- a/src/lempar.c +++ b/src/lempar.c @@ -8,10 +8,10 @@ /* Next is all token values, in a form suitable for use by makeheaders. ** This section will be null unless lemon is run with the -m switch. */ -/* +/* ** These constants (all generated automatically by the parser generator) ** specify the various kinds of tokens (terminals) that the parser -** understands. +** understands. ** ** Each symbol here is a terminal symbol in the grammar. */ @@ -29,7 +29,7 @@ ** and nonterminals. "int" is used otherwise. ** YYNOCODE is a number of type YYCODETYPE which corresponds ** to no legal terminal or nonterminal number. This -** number is used to fill in empty slots of the hash +** number is used to fill in empty slots of the hash ** table. ** YYFALLBACK If defined, this indicates that one or more tokens ** have fall-back values which should be used if the @@ -38,7 +38,7 @@ ** and nonterminal numbers. "unsigned char" is ** used if there are fewer than 250 rules and ** states combined. "int" is used otherwise. -** ParseTOKENTYPE is the data type used for minor tokens given +** ParseTOKENTYPE is the data type used for minor tokens given ** directly to the parser from the tokenizer. ** YYMINORTYPE is the data type used for all minor tokens. ** This is typically a union of many types, one of @@ -62,7 +62,7 @@ /* Next are that tables used to determine what action to take based on the ** current state and lookahead token. These tables are used to implement ** functions that take a state number and lookahead value and return an -** action integer. +** action integer. ** ** Suppose the action integer is N. Then the action is determined as ** follows @@ -87,7 +87,7 @@ ** If the index value yy_shift_ofst[S]+X is out of range or if the value ** yy_lookahead[yy_shift_ofst[S]+X] is not equal to X or if yy_shift_ofst[S] ** is equal to YY_SHIFT_USE_DFLT, it means that the action is not in the table -** and that yy_default[S] should be used instead. +** and that yy_default[S] should be used instead. ** ** The formula above is for computing the action when the lookahead is ** a terminal symbol. If the lookahead is a non-terminal (as occurs after @@ -111,7 +111,7 @@ /* The next table maps tokens into fallback tokens. If a construct ** like the following: -** +** ** %fallback ID X Y Z. ** ** appears in the grammer, then ID becomes a fallback token for X, Y, @@ -158,15 +158,15 @@ typedef struct yyParser yyParser; #ifndef NDEBUG #include <stdio.h> -static FILE *yyTraceFILE = 0; -static char *yyTracePrompt = 0; +static FILE *yyTraceFILE = NULL; +static char *yyTracePrompt = NULL; #endif /* NDEBUG */ #ifndef NDEBUG -/* +/* ** Turn parser tracing on by giving a stream to which to write the trace ** and a prompt to preface each trace message. Tracing is turned off -** by making either argument NULL +** by making either argument NULL ** ** Inputs: ** <ul> @@ -180,18 +180,20 @@ static char *yyTracePrompt = 0; ** Outputs: ** None. */ +#if 0 void ParseTrace(FILE *TraceFILE, char *zTracePrompt){ yyTraceFILE = TraceFILE; yyTracePrompt = zTracePrompt; if( yyTraceFILE==0 ) yyTracePrompt = 0; else if( yyTracePrompt==0 ) yyTraceFILE = 0; } +#endif #endif /* NDEBUG */ #ifndef NDEBUG /* For tracing shifts, the names of all terminals and nonterminals ** are required. The following table supplies these names */ -static const char *yyTokenName[] = { +static const char *yyTokenName[] = { %% }; #endif /* NDEBUG */ @@ -208,9 +210,10 @@ static const char *yyRuleName[] = { ** This function returns the symbolic name associated with a token ** value. */ +#if 0 const char *ParseTokenName(int tokenType){ #ifndef NDEBUG - if( tokenType>0 && tokenType<(sizeof(yyTokenName)/sizeof(yyTokenName[0])) ){ + if( tokenType>0 && (size_t)tokenType<(sizeof(yyTokenName)/sizeof(yyTokenName[0])) ){ return yyTokenName[tokenType]; }else{ return "Unknown"; @@ -219,8 +222,9 @@ const char *ParseTokenName(int tokenType){ return ""; #endif } +#endif -/* +/* ** This function allocates a new parser. ** The only argument is a pointer to a function which works like ** malloc. @@ -251,7 +255,7 @@ static void yy_destructor(YYCODETYPE yymajor, YYMINORTYPE *yypminor){ /* Here is inserted the actions which take place when a ** terminal or non-terminal is destroyed. This can happen ** when the symbol is popped from the stack during a - ** reduce or during error processing or when a parser is + ** reduce or during error processing or when a parser is ** being destroyed before it is finished parsing. ** ** Note: during a reduce, the only symbols destroyed are those @@ -289,7 +293,7 @@ static int yy_pop_parser_stack(yyParser *pParser){ return yymajor; } -/* +/* ** Deallocate and destroy a parser. Destructors are all called for ** all stack elements before shutting the parser down. ** @@ -306,7 +310,7 @@ void ParseFree( void (*freeProc)(void*) /* Function used to reclaim memory */ ){ yyParser *pParser = (yyParser*)p; - if( pParser==0 ) return; + if( pParser==NULL ) return; while( pParser->yyidx>=0 ) yy_pop_parser_stack(pParser); (*freeProc)((void*)pParser); } @@ -325,7 +329,7 @@ static int yy_find_shift_action( ){ int i; int stateno = pParser->yystack[pParser->yyidx].stateno; - + /* if( pParser->yyidx<0 ) return YY_NO_ACTION; */ i = yy_shift_ofst[stateno]; if( i==YY_SHIFT_USE_DFLT ){ @@ -335,7 +339,7 @@ static int yy_find_shift_action( return YY_NO_ACTION; } i += iLookAhead; - if( i<0 || i>=YY_SZ_ACTTAB || yy_lookahead[i]!=iLookAhead ){ + if( i<0 || (size_t)i>=YY_SZ_ACTTAB || yy_lookahead[i]!=iLookAhead ){ #ifdef YYFALLBACK int iFallback; /* Fallback token */ if( iLookAhead<sizeof(yyFallback)/sizeof(yyFallback[0]) @@ -369,7 +373,7 @@ static int yy_find_reduce_action( ){ int i; int stateno = pParser->yystack[pParser->yyidx].stateno; - + i = yy_reduce_ofst[stateno]; if( i==YY_REDUCE_USE_DFLT ){ return yy_default[stateno]; @@ -378,7 +382,7 @@ static int yy_find_reduce_action( return YY_NO_ACTION; } i += iLookAhead; - if( i<0 || i>=YY_SZ_ACTTAB || yy_lookahead[i]!=iLookAhead ){ + if( i<0 || (size_t)i>=YY_SZ_ACTTAB || yy_lookahead[i]!=iLookAhead ){ return yy_default[stateno]; }else{ return yy_action[i]; @@ -455,8 +459,8 @@ static void yy_reduce( ParseARG_FETCH; yymsp = &yypParser->yystack[yypParser->yyidx]; #ifndef NDEBUG - if( yyTraceFILE && yyruleno>=0 - && yyruleno<sizeof(yyRuleName)/sizeof(yyRuleName[0]) ){ + if( yyTraceFILE && yyruleno>=0 + && (size_t)yyruleno<sizeof(yyRuleName)/sizeof(yyRuleName[0]) ){ fprintf(yyTraceFILE, "%sReduce [%s].\n", yyTracePrompt, yyRuleName[yyruleno]); } @@ -512,6 +516,8 @@ static void yy_syntax_error( YYMINORTYPE yyminor /* The minor type of the error token */ ){ ParseARG_FETCH; + UNUSED(yymajor); + UNUSED(yyminor); #define TOKEN (yyminor.yy0) %% ParseARG_STORE; /* Suppress warning about unused %extra_argument variable */ @@ -608,7 +614,7 @@ void Parse( #ifdef YYERRORSYMBOL /* A syntax error has occurred. ** The response to an error depends upon whether or not the - ** grammar defines an error token "ERROR". + ** grammar defines an error token "ERROR". ** ** This is what we do if the grammar does define ERROR: ** diff --git a/src/lighttpd-angel.c b/src/lighttpd-angel.c new file mode 100644 index 0000000..948ca01 --- /dev/null +++ b/src/lighttpd-angel.c @@ -0,0 +1,158 @@ +/** + * angel process for lighttpd + * + * the purpose is the run as root all the time and handle: + * - restart on crash + * - spawn on HUP to allow graceful restart + * - ... + * + * it has to stay safe and small to be trustable + */ + +#include <sys/wait.h> + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include <unistd.h> +#include <time.h> +#include <signal.h> + +#define BINPATH SBIN_DIR"/lighttpd" + +static siginfo_t last_sigterm_info; +static siginfo_t last_sighup_info; + +static volatile sig_atomic_t start_process = 1; +static volatile pid_t pid = -1; + +#define UNUSED(x) ( (void)(x) ) + +static void sigaction_handler(int sig, siginfo_t *si, void *context) { + int exitcode; + + UNUSED(context); + switch (sig) { + case SIGINT: + case SIGTERM: + memcpy(&last_sigterm_info, si, sizeof(*si)); + + /** forward the sig to the child */ + kill(pid, sig); + break; + case SIGHUP: /** do a graceful restart */ + memcpy(&last_sighup_info, si, sizeof(*si)); + + /** do a graceful shutdown on the main process and start a new child */ + kill(pid, SIGINT); + + usleep(5 * 1000); /** wait 5 microsec */ + + start_process = 1; + break; + case SIGCHLD: + /** a child died, de-combie it */ + wait(&exitcode); + break; + } +} + +int main(int argc, char **argv) { + int is_shutdown = 0; + struct sigaction act; + + UNUSED(argc); + + /** + * we are running as root BEWARE + */ + + memset(&act, 0, sizeof(act)); + act.sa_handler = SIG_IGN; + sigaction(SIGPIPE, &act, NULL); + sigaction(SIGUSR1, &act, NULL); + + act.sa_sigaction = sigaction_handler; + sigemptyset(&act.sa_mask); + act.sa_flags = SA_SIGINFO; + + sigaction(SIGINT, &act, NULL); + sigaction(SIGTERM, &act, NULL); + sigaction(SIGHUP, &act, NULL); + sigaction(SIGALRM, &act, NULL); + sigaction(SIGCHLD, &act, NULL); + + /* check that the compiled in path has the right user, + * + * BEWARE: there is a race between the check here and the exec later + */ + + while (!is_shutdown) { + int exitcode = 0; + + if (start_process) { + pid = fork(); + + if (0 == pid) { + /* i'm the child */ + + argv[0] = BINPATH; + + execvp(BINPATH, argv); + + exit(1); + } else if (-1 == pid) { + /** error */ + + return -1; + } + + /* I'm the angel */ + start_process = 0; + } + + if ((pid_t)-1 == waitpid(pid, &exitcode, 0)) { + switch (errno) { + case EINTR: + /* someone sent a signal ... + * do we have to shutdown or restart the process */ + break; + case ECHILD: + /** + * make sure we are not in a race between the signal handler + * and the process restart */ + if (!start_process) is_shutdown = 1; + break; + default: + break; + } + } else { + /** process went away */ + + if (WIFEXITED(exitcode)) { + /** normal exit */ + + is_shutdown = 1; + + fprintf(stderr, "%s.%d: child (pid=%d) exited normally with exitcode: %d\n", + __FILE__, __LINE__, + pid, + WEXITSTATUS(exitcode)); + + } else if (WIFSIGNALED(exitcode)) { + /** got a signal */ + + fprintf(stderr, "%s.%d: child (pid=%d) exited unexpectedly with signal %d, restarting\n", + __FILE__, __LINE__, + pid, + WTERMSIG(exitcode)); + + start_process = 1; + } + } + } + + return 0; +} + @@ -1,4 +1,6 @@ -#define _GNU_SOURCE +#include "base.h" +#include "log.h" +#include "array.h" #include <sys/types.h> @@ -12,206 +14,319 @@ #include <stdarg.h> #include <stdio.h> -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - #ifdef HAVE_SYSLOG_H -#include <syslog.h> +# include <syslog.h> #endif -#include "log.h" -#include "array.h" - #ifdef HAVE_VALGRIND_VALGRIND_H -#include <valgrind/valgrind.h> +# include <valgrind/valgrind.h> #endif #ifndef O_LARGEFILE # define O_LARGEFILE 0 #endif -/** +/* Close fd and _try_ to get a /dev/null for it instead. + * close() alone may trigger some bugs when a + * process opens another file and gets fd = STDOUT_FILENO or STDERR_FILENO + * and later tries to just print on stdout/stderr + * + * Returns 0 on success and -1 on failure (fd gets closed in all cases) + */ +int openDevNull(int fd) { + int tmpfd; + close(fd); +#if defined(__WIN32) + /* Cygwin should work with /dev/null */ + tmpfd = open("nul", O_RDWR); +#else + tmpfd = open("/dev/null", O_RDWR); +#endif + if (tmpfd != -1 && tmpfd != fd) { + dup2(tmpfd, fd); + close(tmpfd); + } + return (tmpfd != -1) ? 0 : -1; +} + +int open_logfile_or_pipe(server *srv, const char* logfile) { + int fd; + + if (logfile[0] == '|') { +#ifdef HAVE_FORK + /* create write pipe and spawn process */ + + int to_log_fds[2]; + + if (pipe(to_log_fds)) { + log_error_write(srv, __FILE__, __LINE__, "ss", "pipe failed: ", strerror(errno)); + return -1; + } + + /* fork, execve */ + switch (fork()) { + case 0: + /* child */ + close(STDIN_FILENO); + + /* dup the filehandle to STDIN */ + if (to_log_fds[0] != STDIN_FILENO) { + if (STDIN_FILENO != dup2(to_log_fds[0], STDIN_FILENO)) { + log_error_write(srv, __FILE__, __LINE__, "ss", + "dup2 failed: ", strerror(errno)); + exit(-1); + } + close(to_log_fds[0]); + } + close(to_log_fds[1]); + +#ifndef FD_CLOEXEC + { + int i; + /* we don't need the client socket */ + for (i = 3; i < 256; i++) { + close(i); + } + } +#endif + + /* close old stderr */ + openDevNull(STDERR_FILENO); + + /* exec the log-process (skip the | ) */ + execl("/bin/sh", "sh", "-c", logfile + 1, NULL); + log_error_write(srv, __FILE__, __LINE__, "sss", + "spawning log process failed: ", strerror(errno), + logfile + 1); + + exit(-1); + break; + case -1: + /* error */ + log_error_write(srv, __FILE__, __LINE__, "ss", "fork failed: ", strerror(errno)); + return -1; + default: + close(to_log_fds[0]); + fd = to_log_fds[1]; + break; + } + +#else + return -1; +#endif + } else if (-1 == (fd = open(logfile, O_APPEND | O_WRONLY | O_CREAT | O_LARGEFILE, 0644))) { + log_error_write(srv, __FILE__, __LINE__, "SSSS", + "opening errorlog '", logfile, + "' failed: ", strerror(errno)); + + return -1; + } + +#ifdef FD_CLOEXEC + fcntl(fd, F_SETFD, FD_CLOEXEC); +#endif + + return fd; +} + + +/** * open the errorlog - * - * we have 3 possibilities: + * + * we have 4 possibilities: * - stderr (default) - * - syslog + * - syslog * - logfile - * + * - pipe + * * if the open failed, report to the user and die - * + * */ int log_error_open(server *srv) { - int fd; - int close_stderr = 1; - #ifdef HAVE_SYSLOG_H /* perhaps someone wants to use syslog() */ openlog("lighttpd", LOG_CONS | LOG_PID, LOG_DAEMON); #endif - srv->errorlog_mode = ERRORLOG_STDERR; - + + srv->errorlog_mode = ERRORLOG_FD; + srv->errorlog_fd = STDERR_FILENO; + if (srv->srvconf.errorlog_use_syslog) { srv->errorlog_mode = ERRORLOG_SYSLOG; } else if (!buffer_is_empty(srv->srvconf.errorlog_file)) { const char *logfile = srv->srvconf.errorlog_file->ptr; - - if (-1 == (srv->errorlog_fd = open(logfile, O_APPEND | O_WRONLY | O_CREAT | O_LARGEFILE, 0644))) { - log_error_write(srv, __FILE__, __LINE__, "SSSS", - "opening errorlog '", logfile, - "' failed: ", strerror(errno)); - + + if (-1 == (srv->errorlog_fd = open_logfile_or_pipe(srv, logfile))) { return -1; } -#ifdef FD_CLOEXEC - /* close fd on exec (cgi) */ - fcntl(srv->errorlog_fd, F_SETFD, FD_CLOEXEC); -#endif - srv->errorlog_mode = ERRORLOG_FILE; + srv->errorlog_mode = (logfile[0] == '|') ? ERRORLOG_PIPE : ERRORLOG_FILE; } - + log_error_write(srv, __FILE__, __LINE__, "s", "server started"); - -#ifdef HAVE_VALGRIND_VALGRIND_H - /* don't close stderr for debugging purposes if run in valgrind */ - if (RUNNING_ON_VALGRIND) close_stderr = 0; + + if (srv->errorlog_mode == ERRORLOG_FD && !srv->srvconf.dont_daemonize) { + /* We can only log to stderr in dont-daemonize mode; + * if we do daemonize and no errorlog file is specified, we log into /dev/null + */ + srv->errorlog_fd = -1; + } + + if (!buffer_is_empty(srv->srvconf.breakagelog_file)) { + int breakage_fd; + const char *logfile = srv->srvconf.breakagelog_file->ptr; + + if (srv->errorlog_mode == ERRORLOG_FD) { + srv->errorlog_fd = dup(STDERR_FILENO); +#ifdef FD_CLOEXEC + fcntl(srv->errorlog_fd, F_SETFD, FD_CLOEXEC); #endif - if (srv->errorlog_mode == ERRORLOG_STDERR) close_stderr = 0; - - /* move stderr to /dev/null */ - if (close_stderr && - -1 != (fd = open("/dev/null", O_WRONLY))) { - close(STDERR_FILENO); - dup2(fd, STDERR_FILENO); - close(fd); + } + + if (-1 == (breakage_fd = open_logfile_or_pipe(srv, logfile))) { + return -1; + } + + if (STDERR_FILENO != breakage_fd) { + dup2(breakage_fd, STDERR_FILENO); + close(breakage_fd); + } + } else if (!srv->srvconf.dont_daemonize) { + /* move stderr to /dev/null */ + openDevNull(STDERR_FILENO); } return 0; } -/** +/** * open the errorlog - * + * * if the open failed, report to the user and die * if no filename is given, use syslog instead - * + * */ int log_error_cycle(server *srv) { - /* only cycle if we are not in syslog-mode */ - + /* only cycle if the error log is a file */ + if (srv->errorlog_mode == ERRORLOG_FILE) { const char *logfile = srv->srvconf.errorlog_file->ptr; /* already check of opening time */ - + int new_fd; - - if (-1 == (new_fd = open(logfile, O_APPEND | O_WRONLY | O_CREAT | O_LARGEFILE, 0644))) { + + if (-1 == (new_fd = open_logfile_or_pipe(srv, logfile))) { /* write to old log */ - log_error_write(srv, __FILE__, __LINE__, "SSSSS", + log_error_write(srv, __FILE__, __LINE__, "SSSSS", "cycling errorlog '", logfile, "' failed: ", strerror(errno), ", falling back to syslog()"); - + close(srv->errorlog_fd); srv->errorlog_fd = -1; -#ifdef HAVE_SYSLOG_H +#ifdef HAVE_SYSLOG_H srv->errorlog_mode = ERRORLOG_SYSLOG; #endif } else { /* ok, new log is open, close the old one */ close(srv->errorlog_fd); srv->errorlog_fd = new_fd; +#ifdef FD_CLOEXEC + /* close fd on exec (cgi) */ + fcntl(srv->errorlog_fd, F_SETFD, FD_CLOEXEC); +#endif } } - - log_error_write(srv, __FILE__, __LINE__, "s", "logfiles cycled"); - + return 0; } int log_error_close(server *srv) { - log_error_write(srv, __FILE__, __LINE__, "s", "server stopped"); - switch(srv->errorlog_mode) { + case ERRORLOG_PIPE: case ERRORLOG_FILE: - close(srv->errorlog_fd); + case ERRORLOG_FD: + if (-1 != srv->errorlog_fd) { + /* don't close STDERR */ + if (STDERR_FILENO != srv->errorlog_fd) + close(srv->errorlog_fd); + srv->errorlog_fd = -1; + } break; case ERRORLOG_SYSLOG: #ifdef HAVE_SYSLOG_H closelog(); #endif break; - case ERRORLOG_STDERR: - break; } - + return 0; } int log_error_write(server *srv, const char *filename, unsigned int line, const char *fmt, ...) { va_list ap; - + switch(srv->errorlog_mode) { + case ERRORLOG_PIPE: case ERRORLOG_FILE: - case ERRORLOG_STDERR: + case ERRORLOG_FD: + if (-1 == srv->errorlog_fd) return 0; /* cache the generated timestamp */ if (srv->cur_ts != srv->last_generated_debug_ts) { buffer_prepare_copy(srv->ts_debug_str, 255); strftime(srv->ts_debug_str->ptr, srv->ts_debug_str->size - 1, "%Y-%m-%d %H:%M:%S", localtime(&(srv->cur_ts))); srv->ts_debug_str->used = strlen(srv->ts_debug_str->ptr) + 1; - + srv->last_generated_debug_ts = srv->cur_ts; } buffer_copy_string_buffer(srv->errorlog_buf, srv->ts_debug_str); - BUFFER_APPEND_STRING_CONST(srv->errorlog_buf, ": ("); + buffer_append_string_len(srv->errorlog_buf, CONST_STR_LEN(": (")); break; case ERRORLOG_SYSLOG: /* syslog is generating its own timestamps */ - BUFFER_COPY_STRING_CONST(srv->errorlog_buf, "("); + buffer_copy_string_len(srv->errorlog_buf, CONST_STR_LEN("(")); break; } - + buffer_append_string(srv->errorlog_buf, filename); - BUFFER_APPEND_STRING_CONST(srv->errorlog_buf, "."); + buffer_append_string_len(srv->errorlog_buf, CONST_STR_LEN(".")); buffer_append_long(srv->errorlog_buf, line); - BUFFER_APPEND_STRING_CONST(srv->errorlog_buf, ") "); - - + buffer_append_string_len(srv->errorlog_buf, CONST_STR_LEN(") ")); + + for(va_start(ap, fmt); *fmt; fmt++) { int d; char *s; buffer *b; off_t o; - + switch(*fmt) { case 's': /* string */ s = va_arg(ap, char *); buffer_append_string(srv->errorlog_buf, s); - BUFFER_APPEND_STRING_CONST(srv->errorlog_buf, " "); + buffer_append_string_len(srv->errorlog_buf, CONST_STR_LEN(" ")); break; case 'b': /* buffer */ b = va_arg(ap, buffer *); buffer_append_string_buffer(srv->errorlog_buf, b); - BUFFER_APPEND_STRING_CONST(srv->errorlog_buf, " "); + buffer_append_string_len(srv->errorlog_buf, CONST_STR_LEN(" ")); break; case 'd': /* int */ d = va_arg(ap, int); buffer_append_long(srv->errorlog_buf, d); - BUFFER_APPEND_STRING_CONST(srv->errorlog_buf, " "); + buffer_append_string_len(srv->errorlog_buf, CONST_STR_LEN(" ")); break; case 'o': /* off_t */ o = va_arg(ap, off_t); buffer_append_off_t(srv->errorlog_buf, o); - BUFFER_APPEND_STRING_CONST(srv->errorlog_buf, " "); + buffer_append_string_len(srv->errorlog_buf, CONST_STR_LEN(" ")); break; case 'x': /* int (hex) */ d = va_arg(ap, int); - BUFFER_APPEND_STRING_CONST(srv->errorlog_buf, "0x"); + buffer_append_string_len(srv->errorlog_buf, CONST_STR_LEN("0x")); buffer_append_long_hex(srv->errorlog_buf, d); - BUFFER_APPEND_STRING_CONST(srv->errorlog_buf, " "); + buffer_append_string_len(srv->errorlog_buf, CONST_STR_LEN(" ")); break; case 'S': /* string */ s = va_arg(ap, char *); @@ -225,9 +340,18 @@ int log_error_write(server *srv, const char *filename, unsigned int line, const d = va_arg(ap, int); buffer_append_long(srv->errorlog_buf, d); break; + case 'O': /* off_t */ + o = va_arg(ap, off_t); + buffer_append_off_t(srv->errorlog_buf, o); + break; + case 'X': /* int (hex) */ + d = va_arg(ap, int); + buffer_append_string_len(srv->errorlog_buf, CONST_STR_LEN("0x")); + buffer_append_long_hex(srv->errorlog_buf, d); + break; case '(': case ')': - case '<': + case '<': case '>': case ',': case ' ': @@ -236,21 +360,19 @@ int log_error_write(server *srv, const char *filename, unsigned int line, const } } va_end(ap); - + switch(srv->errorlog_mode) { + case ERRORLOG_PIPE: case ERRORLOG_FILE: - BUFFER_APPEND_STRING_CONST(srv->errorlog_buf, "\n"); + case ERRORLOG_FD: + buffer_append_string_len(srv->errorlog_buf, CONST_STR_LEN("\n")); write(srv->errorlog_fd, srv->errorlog_buf->ptr, srv->errorlog_buf->used - 1); break; - case ERRORLOG_STDERR: - BUFFER_APPEND_STRING_CONST(srv->errorlog_buf, "\n"); - write(STDERR_FILENO, srv->errorlog_buf->ptr, srv->errorlog_buf->used - 1); - break; case ERRORLOG_SYSLOG: syslog(LOG_ERR, "%s", srv->errorlog_buf->ptr); break; } - + return 0; } @@ -3,11 +3,18 @@ #include "server.h" +/* Close fd and _try_ to get a /dev/null for it instead. + * Returns 0 on success and -1 on failure (fd gets closed in all cases) + */ +int openDevNull(int fd); + #define WP() log_error_write(srv, __FILE__, __LINE__, ""); +int open_logfile_or_pipe(server *srv, const char* logfile); + int log_error_open(server *srv); int log_error_close(server *srv); int log_error_write(server *srv, const char *filename, unsigned int line, const char *fmt, ...); int log_error_cycle(server *srv); - + #endif @@ -24,7 +24,7 @@ documentation and/or software. */ #ifdef HAVE_CONFIG_H -#include "config.h" +# include "config.h" #endif #ifndef USE_OPENSSL @@ -52,9 +52,9 @@ documentation and/or software. #define S43 15 #define S44 21 -static void MD5Transform (UINT4 [4], unsigned char [64]); +static void li_MD5Transform (UINT4 [4], const unsigned char [64]); static void Encode (unsigned char *, UINT4 *, unsigned int); -static void Decode (UINT4 *, unsigned char *, unsigned int); +static void Decode (UINT4 *, const unsigned char *, unsigned int); #ifdef HAVE_MEMCPY #define MD5_memcpy(output, input, len) memcpy((output), (input), (len)) @@ -110,8 +110,7 @@ Rotation is separate from addition to prevent recomputation. /* MD5 initialization. Begins an MD5 operation, writing a new context. */ -void MD5_Init (context) -MD5_CTX *context; /* context */ +void li_MD5_Init (li_MD5_CTX *context) { context->count[0] = context->count[1] = 0; /* Load magic initialization constants. @@ -126,12 +125,10 @@ MD5_CTX *context; /* context */ operation, processing another message block, and updating the context. */ -void MD5_Update (context, input, inputLen) -MD5_CTX *context; /* context */ -unsigned char *input; /* input block */ -unsigned int inputLen; /* length of input block */ +void li_MD5_Update (li_MD5_CTX *context, const void *_input, unsigned int inputLen) { unsigned int i, ndx, partLen; + const unsigned char *input = (const unsigned char*) _input; /* Compute number of bytes mod 64 */ ndx = (unsigned int)((context->count[0] >> 3) & 0x3F); @@ -150,10 +147,10 @@ unsigned int inputLen; /* length of input block */ if (inputLen >= partLen) { MD5_memcpy ((POINTER)&context->buffer[ndx], (POINTER)input, partLen); - MD5Transform (context->state, context->buffer); + li_MD5Transform (context->state, context->buffer); for (i = partLen; i + 63 < inputLen; i += 64) - MD5Transform (context->state, &input[i]); + li_MD5Transform (context->state, &input[i]); ndx = 0; } @@ -169,9 +166,7 @@ unsigned int inputLen; /* length of input block */ /* MD5 finalization. Ends an MD5 message-digest operation, writing the the message digest and zeroizing the context. */ -void MD5_Final (digest, context) -unsigned char digest[16]; /* message digest */ -MD5_CTX *context; /* context */ +void li_MD5_Final (unsigned char digest[16], li_MD5_CTX *context) { unsigned char bits[8]; unsigned int ndx, padLen; @@ -183,10 +178,10 @@ MD5_CTX *context; /* context */ */ ndx = (unsigned int)((context->count[0] >> 3) & 0x3f); padLen = (ndx < 56) ? (56 - ndx) : (120 - ndx); - MD5_Update (context, PADDING, padLen); + li_MD5_Update (context, PADDING, padLen); /* Append length (before padding) */ - MD5_Update (context, bits, 8); + li_MD5_Update (context, bits, 8); /* Store state in digest */ Encode (digest, context->state, 16); @@ -198,9 +193,7 @@ MD5_CTX *context; /* context */ /* MD5 basic transformation. Transforms state based on block. */ -static void MD5Transform (state, block) -UINT4 state[4]; -unsigned char block[64]; +static void li_MD5Transform (UINT4 state[4], const unsigned char block[64]) { UINT4 a = state[0], b = state[1], c = state[2], d = state[3], x[16]; @@ -293,10 +286,7 @@ unsigned char block[64]; /* Encodes input (UINT4) into output (unsigned char). Assumes len is a multiple of 4. */ -static void Encode (output, input, len) -unsigned char *output; -UINT4 *input; -unsigned int len; +static void Encode (unsigned char *output, UINT4 *input, unsigned int len) { unsigned int i, j; @@ -311,10 +301,7 @@ unsigned int len; /* Decodes input (unsigned char) into output (UINT4). Assumes len is a multiple of 4. */ -static void Decode (output, input, len) -UINT4 *output; -unsigned char *input; -unsigned int len; +static void Decode (UINT4 *output, const unsigned char *input, unsigned int len) { unsigned int i, j; @@ -326,10 +313,7 @@ unsigned int len; /* Note: Replace "for loop" with standard memcpy if possible. */ #ifndef HAVE_MEMCPY -static void MD5_memcpy (output, input, len) -POINTER output; -POINTER input; -unsigned int len; +static void MD5_memcpy (POINTER output, POINTER input, unsigned int len) { unsigned int i; @@ -341,10 +325,7 @@ unsigned int len; /* Note: Replace "for loop" with standard memset if possible. */ #ifndef HAVE_MEMSET -static void MD5_memset (output, value, len) -POINTER output; -int value; -unsigned int len; +static void MD5_memset (POINTER output, int value, unsigned int len) { unsigned int i; @@ -39,9 +39,8 @@ typedef struct { UINT4 state[4]; /* state (ABCD) */ UINT4 count[2]; /* number of bits, modulo 2^64 (lsb first) */ unsigned char buffer[64]; /* input buffer */ -} MD5_CTX; - -void MD5_Init (MD5_CTX *); -void MD5_Update (MD5_CTX *, unsigned char *, unsigned int); -void MD5_Final (unsigned char [16], MD5_CTX *); +} li_MD5_CTX; +void li_MD5_Init (li_MD5_CTX *); +void li_MD5_Update (li_MD5_CTX *, const void *, unsigned int); +void li_MD5_Final (unsigned char [16], li_MD5_CTX *); diff --git a/src/mod_access.c b/src/mod_access.c index f3f7071..3902435 100644 --- a/src/mod_access.c +++ b/src/mod_access.c @@ -1,83 +1,83 @@ -#include <ctype.h> -#include <stdlib.h> -#include <string.h> - #include "base.h" #include "log.h" #include "buffer.h" #include "plugin.h" +#include <ctype.h> +#include <stdlib.h> +#include <string.h> + typedef struct { array *access_deny; } plugin_config; typedef struct { PLUGIN_DATA; - + plugin_config **config_storage; - - plugin_config conf; + + plugin_config conf; } plugin_data; INIT_FUNC(mod_access_init) { plugin_data *p; - + p = calloc(1, sizeof(*p)); - + return p; } FREE_FUNC(mod_access_free) { plugin_data *p = p_d; - + UNUSED(srv); if (!p) return HANDLER_GO_ON; - + if (p->config_storage) { size_t i; for (i = 0; i < srv->config_context->used; i++) { plugin_config *s = p->config_storage[i]; - + array_free(s->access_deny); - + free(s); } free(p->config_storage); } - + free(p); - + return HANDLER_GO_ON; } SETDEFAULTS_FUNC(mod_access_set_defaults) { plugin_data *p = p_d; size_t i = 0; - - config_values_t cv[] = { + + config_values_t cv[] = { { "url.access-deny", NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION }, { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET } }; - + p->config_storage = calloc(1, srv->config_context->used * sizeof(specific_config *)); - + for (i = 0; i < srv->config_context->used; i++) { plugin_config *s; - + s = calloc(1, sizeof(plugin_config)); s->access_deny = array_init(); - + cv[0].destination = s->access_deny; - + p->config_storage[i] = s; - + if (0 != config_insert_values_global(srv, ((data_config *)srv->config_context->data[i])->value, cv)) { return HANDLER_ERROR; } } - + return HANDLER_GO_ON; } @@ -88,80 +88,105 @@ static int mod_access_patch_connection(server *srv, connection *con, plugin_data plugin_config *s = p->config_storage[0]; PATCH(access_deny); - + /* skip the first, the global context */ for (i = 1; i < srv->config_context->used; i++) { data_config *dc = (data_config *)srv->config_context->data[i]; s = p->config_storage[i]; - + /* condition didn't match */ if (!config_check_cond(srv, con, dc)) continue; - + /* merge config */ for (j = 0; j < dc->value->used; j++) { data_unset *du = dc->value->data[j]; - + if (buffer_is_equal_string(du->key, CONST_STR_LEN("url.access-deny"))) { PATCH(access_deny); } } } - + return 0; } #undef PATCH +/** + * URI handler + * + * we will get called twice: + * - after the clean up of the URL and + * - after the pathinfo checks are done + * + * this handles the issue of trailing slashes + */ URIHANDLER_FUNC(mod_access_uri_handler) { plugin_data *p = p_d; int s_len; size_t k; - + if (con->uri.path->used == 0) return HANDLER_GO_ON; - + mod_access_patch_connection(srv, con, p); - + s_len = con->uri.path->used - 1; - + + if (con->conf.log_request_handling) { + log_error_write(srv, __FILE__, __LINE__, "s", + "-- mod_access_uri_handler called"); + } + for (k = 0; k < p->conf.access_deny->used; k++) { data_string *ds = (data_string *)p->conf.access_deny->data[k]; int ct_len = ds->value->used - 1; - + int denied = 0; + + if (ct_len > s_len) continue; - if (ds->value->used == 0) continue; /* if we have a case-insensitive FS we have to lower-case the URI here too */ if (con->conf.force_lowercase_filenames) { if (0 == strncasecmp(con->uri.path->ptr + s_len - ct_len, ds->value->ptr, ct_len)) { - con->http_status = 403; - - return HANDLER_FINISHED; + denied = 1; } } else { if (0 == strncmp(con->uri.path->ptr + s_len - ct_len, ds->value->ptr, ct_len)) { - con->http_status = 403; - - return HANDLER_FINISHED; + denied = 1; + } + } + + if (denied) { + con->http_status = 403; + con->mode = DIRECT; + + if (con->conf.log_request_handling) { + log_error_write(srv, __FILE__, __LINE__, "sb", + "url denied as we match:", ds->value); } + + return HANDLER_FINISHED; } } - + /* not found */ return HANDLER_GO_ON; } +int mod_access_plugin_init(plugin *p); int mod_access_plugin_init(plugin *p) { p->version = LIGHTTPD_VERSION_ID; p->name = buffer_init_string("access"); - + p->init = mod_access_init; p->set_defaults = mod_access_set_defaults; - p->handle_uri_clean = mod_access_uri_handler; + p->handle_uri_clean = mod_access_uri_handler; + p->handle_subrequest_start = mod_access_uri_handler; p->cleanup = mod_access_free; - + p->data = NULL; - + return 0; } diff --git a/src/mod_accesslog.c b/src/mod_accesslog.c index d77c889..6d6c173 100644 --- a/src/mod_accesslog.c +++ b/src/mod_accesslog.c @@ -1,4 +1,12 @@ -#define _GNU_SOURCE +#include "base.h" +#include "log.h" +#include "buffer.h" + +#include "plugin.h" + +#include "inet_ntop_cache.h" + +#include "sys-socket.h" #include <sys/types.h> #include <sys/stat.h> @@ -13,23 +21,13 @@ #include <stdio.h> -#include "base.h" -#include "log.h" -#include "buffer.h" - -#include "plugin.h" - -#include "inet_ntop_cache.h" - -#include "sys-socket.h" - #ifdef HAVE_SYSLOG_H # include <syslog.h> #endif typedef struct { char key; - enum { + enum { FORMAT_UNSET, FORMAT_UNSUPPORTED, FORMAT_PERCENT, @@ -41,7 +39,7 @@ typedef struct { FORMAT_STATUS, FORMAT_BYTES_OUT_NO_HEADER, FORMAT_HEADER, - + FORMAT_REMOTE_ADDR, FORMAT_LOCAL_ADDR, FORMAT_COOKIE, @@ -59,20 +57,20 @@ typedef struct { FORMAT_CONNECTION_STATUS, FORMAT_BYTES_IN, FORMAT_BYTES_OUT, - + FORMAT_RESPONSE_HEADER } type; } format_mapping; /** - * - * + * + * * "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" - * + * */ -const format_mapping fmap[] = -{ +static const format_mapping fmap[] = +{ { '%', FORMAT_PERCENT }, { 'h', FORMAT_REMOTE_HOST }, { 'l', FORMAT_REMOTE_IDENT }, @@ -82,7 +80,7 @@ const format_mapping fmap[] = { 's', FORMAT_STATUS }, { 'b', FORMAT_BYTES_OUT_NO_HEADER }, { 'i', FORMAT_HEADER }, - + { 'a', FORMAT_REMOTE_ADDR }, { 'A', FORMAT_LOCAL_ADDR }, { 'B', FORMAT_BYTES_OUT_NO_HEADER }, @@ -103,23 +101,23 @@ const format_mapping fmap[] = { 'X', FORMAT_CONNECTION_STATUS }, { 'I', FORMAT_BYTES_IN }, { 'O', FORMAT_BYTES_OUT }, - + { 'o', FORMAT_RESPONSE_HEADER }, - + { '\0', FORMAT_UNSET } }; typedef struct { enum { FIELD_UNSET, FIELD_STRING, FIELD_FORMAT } type; - + buffer *string; int field; } format_field; typedef struct { format_field **ptr; - + size_t used; size_t size; } format_fields; @@ -128,226 +126,312 @@ typedef struct { buffer *access_logfile; buffer *format; unsigned short use_syslog; - - + + int log_access_fd; time_t last_generated_accesslog_ts; time_t *last_generated_accesslog_ts_ptr; - - + + buffer *access_logbuffer; buffer *ts_accesslog_str; - + buffer *ts_accesslog_fmt_str; + unsigned short append_tz_offset; + format_fields *parsed_format; } plugin_config; typedef struct { PLUGIN_DATA; - + plugin_config **config_storage; - plugin_config conf; + plugin_config conf; } plugin_data; INIT_FUNC(mod_accesslog_init) { plugin_data *p; - + p = calloc(1, sizeof(*p)); - + return p; } -int accesslog_parse_format(server *srv, format_fields *fields, buffer *format) { +static void accesslog_append_escaped(buffer *dest, buffer *str) { + char *ptr, *start, *end; + + /* replaces non-printable chars with \xHH where HH is the hex representation of the byte */ + /* exceptions: " => \", \ => \\, whitespace chars => \n \t etc. */ + if (str->used == 0) return; + buffer_prepare_append(dest, str->used - 1); + + for (ptr = start = str->ptr, end = str->ptr + str->used - 1; ptr < end; ptr++) { + char const c = *ptr; + if (c >= ' ' && c <= '~' && c != '"' && c != '\\') { + /* nothing to change, add later as one block */ + } else { + /* copy previous part */ + if (start < ptr) { + buffer_append_string_len(dest, start, ptr - start); + } + start = ptr + 1; + + switch (c) { + case '"': + BUFFER_APPEND_STRING_CONST(dest, "\\\""); + break; + case '\\': + BUFFER_APPEND_STRING_CONST(dest, "\\\\"); + break; + case '\b': + BUFFER_APPEND_STRING_CONST(dest, "\\b"); + break; + case '\n': + BUFFER_APPEND_STRING_CONST(dest, "\\n"); + break; + case '\r': + BUFFER_APPEND_STRING_CONST(dest, "\\r"); + break; + case '\t': + BUFFER_APPEND_STRING_CONST(dest, "\\t"); + break; + case '\v': + BUFFER_APPEND_STRING_CONST(dest, "\\v"); + break; + default: { + /* non printable char => \xHH */ + char hh[5] = {'\\','x',0,0,0}; + char h = c / 16; + hh[2] = (h > 9) ? (h - 10 + 'A') : (h + '0'); + h = c % 16; + hh[3] = (h > 9) ? (h - 10 + 'A') : (h + '0'); + buffer_append_string_len(dest, &hh[0], 4); + } + break; + } + } + } + + if (start < end) { + buffer_append_string_len(dest, start, end - start); + } +} + +static int accesslog_parse_format(server *srv, format_fields *fields, buffer *format) { size_t i, j, k = 0, start = 0; - + + if (format->used == 0) return -1; + for (i = 0; i < format->used - 1; i++) { - switch(format->ptr[i]) { case '%': - if (start != i) { - /* copy the string */ + if (i > 0 && start != i) { + /* copy the string before this % */ if (fields->size == 0) { fields->size = 16; fields->used = 0; - fields->ptr = malloc(fields->size * sizeof(format_fields * )); + fields->ptr = malloc(fields->size * sizeof(format_field * )); } else if (fields->used == fields->size) { fields->size += 16; - fields->ptr = realloc(fields->ptr, fields->size * sizeof(format_fields * )); + fields->ptr = realloc(fields->ptr, fields->size * sizeof(format_field * )); } - - fields->ptr[fields->used] = malloc(sizeof(format_fields)); + + fields->ptr[fields->used] = malloc(sizeof(format_field)); fields->ptr[fields->used]->type = FIELD_STRING; fields->ptr[fields->used]->string = buffer_init(); - + buffer_copy_string_len(fields->ptr[fields->used]->string, format->ptr + start, i - start); - + fields->used++; } - - + /* we need a new field */ - + if (fields->size == 0) { fields->size = 16; fields->used = 0; - fields->ptr = malloc(fields->size * sizeof(format_fields * )); + fields->ptr = malloc(fields->size * sizeof(format_field * )); } else if (fields->used == fields->size) { fields->size += 16; - fields->ptr = realloc(fields->ptr, fields->size * sizeof(format_fields * )); + fields->ptr = realloc(fields->ptr, fields->size * sizeof(format_field * )); } - + /* search for the terminating command */ switch (format->ptr[i+1]) { case '>': case '<': - /* only for s */ - + /* after the } has to be a character */ + if (format->ptr[i+2] == '\0') { + log_error_write(srv, __FILE__, __LINE__, "s", "%< and %> have to be followed by a format-specifier"); + return -1; + } + + for (j = 0; fmap[j].key != '\0'; j++) { if (fmap[j].key != format->ptr[i+2]) continue; - + /* found key */ - - fields->ptr[fields->used] = malloc(sizeof(format_fields)); + + fields->ptr[fields->used] = malloc(sizeof(format_field)); fields->ptr[fields->used]->type = FIELD_FORMAT; fields->ptr[fields->used]->field = fmap[j].type; fields->ptr[fields->used]->string = NULL; - + fields->used++; - + break; } - + if (fmap[j].key == '\0') { - log_error_write(srv, __FILE__, __LINE__, "ss", "config: ", "failed"); + log_error_write(srv, __FILE__, __LINE__, "s", "%< and %> have to be followed by a valid format-specifier"); return -1; } - + start = i + 3; - + i = start - 1; /* skip the string */ + break; case '{': /* go forward to } */ - + for (k = i+2; k < format->used - 1; k++) { if (format->ptr[k] == '}') break; } - + if (k == format->used - 1) { - log_error_write(srv, __FILE__, __LINE__, "ss", "config: ", "failed"); + log_error_write(srv, __FILE__, __LINE__, "s", "%{ has to be terminated by a }"); return -1; } + + /* after the } has to be a character */ if (format->ptr[k+1] == '\0') { - log_error_write(srv, __FILE__, __LINE__, "ss", "config: ", "failed"); + log_error_write(srv, __FILE__, __LINE__, "s", "%{...} has to be followed by a format-specifier"); + return -1; + } + + if (k == i + 2) { + log_error_write(srv, __FILE__, __LINE__, "s", "%{...} has to be contain a string"); return -1; } - + for (j = 0; fmap[j].key != '\0'; j++) { if (fmap[j].key != format->ptr[k+1]) continue; - + /* found key */ - - fields->ptr[fields->used] = malloc(sizeof(format_fields)); + + fields->ptr[fields->used] = malloc(sizeof(format_field)); fields->ptr[fields->used]->type = FIELD_FORMAT; fields->ptr[fields->used]->field = fmap[j].type; fields->ptr[fields->used]->string = buffer_init(); - + buffer_copy_string_len(fields->ptr[fields->used]->string, format->ptr + i + 2, k - (i + 2)); - + fields->used++; - + break; } - + if (fmap[j].key == '\0') { - log_error_write(srv, __FILE__, __LINE__, "ss", "config: ", "failed"); + log_error_write(srv, __FILE__, __LINE__, "s", "%{...} has to be followed by a valid format-specifier"); return -1; } - + start = k + 2; - + i = start - 1; /* skip the string */ + break; default: + /* after the % has to be a character */ + if (format->ptr[i+1] == '\0') { + log_error_write(srv, __FILE__, __LINE__, "s", "% has to be followed by a format-specifier"); + return -1; + } + for (j = 0; fmap[j].key != '\0'; j++) { if (fmap[j].key != format->ptr[i+1]) continue; - + /* found key */ - - fields->ptr[fields->used] = malloc(sizeof(format_fields)); + + fields->ptr[fields->used] = malloc(sizeof(format_field)); fields->ptr[fields->used]->type = FIELD_FORMAT; fields->ptr[fields->used]->field = fmap[j].type; fields->ptr[fields->used]->string = NULL; - + fields->used++; - + break; } - + if (fmap[j].key == '\0') { - log_error_write(srv, __FILE__, __LINE__, "ss", "config: ", "failed"); + log_error_write(srv, __FILE__, __LINE__, "s", "% has to be followed by a valid format-specifier"); return -1; } - + start = i + 2; - + i = start - 1; /* skip the string */ + break; } - + break; } } - + if (start < i) { /* copy the string */ if (fields->size == 0) { fields->size = 16; fields->used = 0; - fields->ptr = malloc(fields->size * sizeof(format_fields * )); + fields->ptr = malloc(fields->size * sizeof(format_field * )); } else if (fields->used == fields->size) { fields->size += 16; - fields->ptr = realloc(fields->ptr, fields->size * sizeof(format_fields * )); + fields->ptr = realloc(fields->ptr, fields->size * sizeof(format_field * )); } - - fields->ptr[fields->used] = malloc(sizeof(format_fields)); + + fields->ptr[fields->used] = malloc(sizeof(format_field)); fields->ptr[fields->used]->type = FIELD_STRING; fields->ptr[fields->used]->string = buffer_init(); - + buffer_copy_string_len(fields->ptr[fields->used]->string, format->ptr + start, i - start); - + fields->used++; } - + return 0; } FREE_FUNC(mod_accesslog_free) { plugin_data *p = p_d; size_t i; - + if (!p) return HANDLER_GO_ON; - + if (p->config_storage) { - + for (i = 0; i < srv->config_context->used; i++) { plugin_config *s = p->config_storage[i]; if (!s) continue; - + if (s->access_logbuffer->used) { if (s->use_syslog) { # ifdef HAVE_SYSLOG_H - syslog(LOG_INFO, "%*s", s->access_logbuffer->used - 1, s->access_logbuffer->ptr); + if (s->access_logbuffer->used > 2) { + syslog(LOG_INFO, "%*s", (int) s->access_logbuffer->used - 2, s->access_logbuffer->ptr); + } # endif } else if (s->log_access_fd != -1) { write(s->log_access_fd, s->access_logbuffer->ptr, s->access_logbuffer->used - 1); } } - + if (s->log_access_fd != -1) close(s->log_access_fd); - + buffer_free(s->ts_accesslog_str); + buffer_free(s->ts_accesslog_fmt_str); buffer_free(s->access_logbuffer); buffer_free(s->format); buffer_free(s->access_logfile); - + if (s->parsed_format) { size_t j; for (j = 0; j < s->parsed_format->used; j++) { @@ -357,82 +441,108 @@ FREE_FUNC(mod_accesslog_free) { free(s->parsed_format->ptr); free(s->parsed_format); } - + free(s); } - + free(p->config_storage); } - + free(p); - + return HANDLER_GO_ON; } SETDEFAULTS_FUNC(log_access_open) { plugin_data *p = p_d; size_t i = 0; - - config_values_t cv[] = { + + config_values_t cv[] = { { "accesslog.filename", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, { "accesslog.use-syslog", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, { "accesslog.format", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET } }; - + if (!p) return HANDLER_ERROR; - + p->config_storage = calloc(1, srv->config_context->used * sizeof(specific_config *)); - + for (i = 0; i < srv->config_context->used; i++) { plugin_config *s; - + s = calloc(1, sizeof(plugin_config)); s->access_logfile = buffer_init(); s->format = buffer_init(); s->access_logbuffer = buffer_init(); s->ts_accesslog_str = buffer_init(); + s->ts_accesslog_fmt_str = buffer_init(); s->log_access_fd = -1; s->last_generated_accesslog_ts = 0; s->last_generated_accesslog_ts_ptr = &(s->last_generated_accesslog_ts); - - + + cv[0].destination = s->access_logfile; cv[1].destination = &(s->use_syslog); cv[2].destination = s->format; - + p->config_storage[i] = s; - + if (0 != config_insert_values_global(srv, ((data_config *)srv->config_context->data[i])->value, cv)) { return HANDLER_ERROR; } - + if (i == 0 && buffer_is_empty(s->format)) { /* set a default logfile string */ - - buffer_copy_string(s->format, "%h %V %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\""); + + buffer_copy_string_len(s->format, CONST_STR_LEN("%h %V %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"")); } - + /* parse */ - + if (s->format->used) { + size_t j, count; + s->parsed_format = calloc(1, sizeof(*(s->parsed_format))); - + if (-1 == accesslog_parse_format(srv, s->parsed_format, s->format)) { - log_error_write(srv, __FILE__, __LINE__, "sb", + log_error_write(srv, __FILE__, __LINE__, "sb", "parsing accesslog-definition failed:", s->format); return HANDLER_ERROR; } + + /* make sure they didn't try to send the timestamp in twice... + * also, save the format string in a different variable (this + * will save a few conditionals later) + */ + count = 0; + for (j = 0; j < s->parsed_format->used; j++) { + if (FIELD_FORMAT == s->parsed_format->ptr[j]->type) { + if (FORMAT_TIMESTAMP == s->parsed_format->ptr[j]->field) { + if (!buffer_is_empty(s->parsed_format->ptr[j]->string)) { + buffer_copy_string(s->ts_accesslog_fmt_str, s->parsed_format->ptr[j]->string->ptr); + } + + if (++count > 1) { + log_error_write(srv, __FILE__, __LINE__, "sb", + "you may not use the timestamp twice in the same access log:", s->format); + + return HANDLER_ERROR; + } + } + } + } + #if 0 - /* debugging */ + /* debugging */ for (j = 0; j < s->parsed_format->used; j++) { switch (s->parsed_format->ptr[j]->type) { case FIELD_FORMAT: - log_error_write(srv, __FILE__, __LINE__, "ssds", + log_error_write(srv, __FILE__, __LINE__, "ssds", "config:", "format", s->parsed_format->ptr[j]->field, - s->parsed_format->ptr[j]->string ? + s->parsed_format->ptr[j]->string ? s->parsed_format->ptr[j]->string->ptr : "" ); break; case FIELD_STRING: @@ -444,81 +554,29 @@ SETDEFAULTS_FUNC(log_access_open) { } #endif } - + + s->append_tz_offset = 0; + if (buffer_is_empty(s->ts_accesslog_fmt_str)) { +#if defined(HAVE_STRUCT_TM_GMTOFF) + BUFFER_COPY_STRING_CONST(s->ts_accesslog_fmt_str, "[%d/%b/%Y:%H:%M:%S "); + s->append_tz_offset = 1; +#else + BUFFER_COPY_STRING_CONST(s->ts_accesslog_fmt_str, "[%d/%b/%Y:%H:%M:%S +0000]"); +#endif + } + if (s->use_syslog) { /* ignore the next checks */ continue; } - - if (buffer_is_empty(s->access_logfile)) continue; - - if (s->access_logfile->ptr[0] == '|') { -#ifdef HAVE_FORK - /* create write pipe and spawn process */ - - int to_log_fds[2]; - pid_t pid; - - if (pipe(to_log_fds)) { - log_error_write(srv, __FILE__, __LINE__, "ss", "pipe failed: ", strerror(errno)); - return HANDLER_ERROR; - } - - /* fork, execve */ - switch (pid = fork()) { - case 0: - /* child */ - - close(STDIN_FILENO); - dup2(to_log_fds[0], STDIN_FILENO); - close(to_log_fds[0]); - /* not needed */ - close(to_log_fds[1]); - - /* we don't need the client socket */ - for (i = 3; i < 256; i++) { - close(i); - } - - /* exec the log-process (skip the | ) - * - */ - - execl("/bin/sh", "sh", "-c", s->access_logfile->ptr + 1, NULL); - log_error_write(srv, __FILE__, __LINE__, "sss", - "spawning log-process failed: ", strerror(errno), - s->access_logfile->ptr + 1); - - exit(-1); - break; - case -1: - /* error */ - log_error_write(srv, __FILE__, __LINE__, "ss", "fork failed: ", strerror(errno)); - break; - default: - close(to_log_fds[0]); - - s->log_access_fd = to_log_fds[1]; - - break; - } -#else - return -1; -#endif - } else if (-1 == (s->log_access_fd = - open(s->access_logfile->ptr, O_APPEND | O_WRONLY | O_CREAT | O_LARGEFILE, 0644))) { - - log_error_write(srv, __FILE__, __LINE__, "ssb", - "opening access-log failed:", - strerror(errno), s->access_logfile); - + if (s->access_logfile->used < 2) continue; + + if (-1 == (s->log_access_fd = open_logfile_or_pipe(srv, s->access_logfile->ptr))) return HANDLER_ERROR; - } - fcntl(s->log_access_fd, F_SETFD, FD_CLOEXEC); - + } - + return HANDLER_GO_ON; } @@ -527,38 +585,44 @@ SIGHUP_FUNC(log_access_cycle) { size_t i; if (!p->config_storage) return HANDLER_GO_ON; - + for (i = 0; i < srv->config_context->used; i++) { plugin_config *s = p->config_storage[i]; if (s->access_logbuffer->used) { if (s->use_syslog) { #ifdef HAVE_SYSLOG_H - syslog(LOG_INFO, "%*s", s->access_logbuffer->used - 1, s->access_logbuffer->ptr); + if (s->access_logbuffer->used > 2) { + /* syslog appends a \n on its own */ + syslog(LOG_INFO, "%*s", (int) s->access_logbuffer->used - 2, s->access_logbuffer->ptr); + } #endif } else if (s->log_access_fd != -1) { write(s->log_access_fd, s->access_logbuffer->ptr, s->access_logbuffer->used - 1); } - + buffer_reset(s->access_logbuffer); } - + if (s->use_syslog == 0 && - !buffer_is_empty(s->access_logfile) && + s->access_logfile->used > 1 && s->access_logfile->ptr[0] != '|') { - + close(s->log_access_fd); - - if (-1 == (s->log_access_fd = + + if (-1 == (s->log_access_fd = open(s->access_logfile->ptr, O_APPEND | O_WRONLY | O_CREAT | O_LARGEFILE, 0644))) { - + log_error_write(srv, __FILE__, __LINE__, "ss", "cycling access-log failed:", strerror(errno)); - + return HANDLER_ERROR; } +#ifdef FD_CLOEXEC + fcntl(s->log_access_fd, F_SETFD, FD_CLOEXEC); +#endif } } - + return HANDLER_GO_ON; } @@ -567,43 +631,48 @@ SIGHUP_FUNC(log_access_cycle) { static int mod_accesslog_patch_connection(server *srv, connection *con, plugin_data *p) { size_t i, j; plugin_config *s = p->config_storage[0]; - + PATCH(access_logfile); PATCH(format); PATCH(log_access_fd); PATCH(last_generated_accesslog_ts_ptr); PATCH(access_logbuffer); PATCH(ts_accesslog_str); + PATCH(ts_accesslog_fmt_str); + PATCH(append_tz_offset); PATCH(parsed_format); PATCH(use_syslog); - + /* skip the first, the global context */ for (i = 1; i < srv->config_context->used; i++) { data_config *dc = (data_config *)srv->config_context->data[i]; s = p->config_storage[i]; - + /* condition didn't match */ if (!config_check_cond(srv, con, dc)) continue; - + /* merge config */ for (j = 0; j < dc->value->used; j++) { data_unset *du = dc->value->data[j]; - + if (buffer_is_equal_string(du->key, CONST_STR_LEN("accesslog.filename"))) { PATCH(access_logfile); PATCH(log_access_fd); - PATCH(last_generated_accesslog_ts_ptr); PATCH(access_logbuffer); - PATCH(ts_accesslog_str); } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("accesslog.format"))) { PATCH(format); PATCH(parsed_format); + PATCH(last_generated_accesslog_ts_ptr); + PATCH(ts_accesslog_str); + PATCH(ts_accesslog_fmt_str); + PATCH(append_tz_offset); } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("accesslog.use-syslog"))) { PATCH(use_syslog); + PATCH(access_logbuffer); } } } - + return 0; } #undef PATCH @@ -612,17 +681,20 @@ REQUESTDONE_FUNC(log_access_write) { plugin_data *p = p_d; buffer *b; size_t j; - + int newts = 0; data_string *ds; - + mod_accesslog_patch_connection(srv, con, p); - + + /* No output device, nothing to do */ + if (!p->conf.use_syslog && p->conf.log_access_fd == -1) return HANDLER_GO_ON; + b = p->conf.access_logbuffer; if (b->used == 0) { - buffer_copy_string(b, ""); + buffer_copy_string_len(b, CONST_STR_LEN("")); } - + for (j = 0; j < p->conf.parsed_format->used; j++) { switch(p->conf.parsed_format->ptr[j]->type) { case FIELD_STRING: @@ -631,122 +703,131 @@ REQUESTDONE_FUNC(log_access_write) { case FIELD_FORMAT: switch(p->conf.parsed_format->ptr[j]->field) { case FORMAT_TIMESTAMP: - + /* cache the generated timestamp */ if (srv->cur_ts != *(p->conf.last_generated_accesslog_ts_ptr)) { struct tm tm; #if defined(HAVE_STRUCT_TM_GMTOFF) long scd, hrs, min; #endif - + buffer_prepare_copy(p->conf.ts_accesslog_str, 255); #if defined(HAVE_STRUCT_TM_GMTOFF) # ifdef HAVE_LOCALTIME_R localtime_r(&(srv->cur_ts), &tm); - strftime(p->conf.ts_accesslog_str->ptr, p->conf.ts_accesslog_str->size - 1, "[%d/%b/%Y:%H:%M:%S ", &tm); -# else - strftime(p->conf.ts_accesslog_str->ptr, p->conf.ts_accesslog_str->size - 1, "[%d/%b/%Y:%H:%M:%S ", localtime_r(&(srv->cur_ts))); -# endif + strftime(p->conf.ts_accesslog_str->ptr, p->conf.ts_accesslog_str->size - 1, p->conf.ts_accesslog_fmt_str->ptr, &tm); +# else /* HAVE_LOCALTIME_R */ + strftime(p->conf.ts_accesslog_str->ptr, p->conf.ts_accesslog_str->size - 1, p->conf.ts_accesslog_fmt_str->ptr, localtime_r(&(srv->cur_ts))); +# endif /* HAVE_LOCALTIME_R */ p->conf.ts_accesslog_str->used = strlen(p->conf.ts_accesslog_str->ptr) + 1; - - buffer_append_string(p->conf.ts_accesslog_str, tm.tm_gmtoff >= 0 ? "+" : "-"); - - scd = abs(tm.tm_gmtoff); - hrs = scd / 3600; - min = (scd % 3600) / 60; - - /* hours */ - if (hrs < 10) buffer_append_string(p->conf.ts_accesslog_str, "0"); - buffer_append_long(p->conf.ts_accesslog_str, hrs); - - if (min < 10) buffer_append_string(p->conf.ts_accesslog_str, "0"); - buffer_append_long(p->conf.ts_accesslog_str, min); - BUFFER_APPEND_STRING_CONST(p->conf.ts_accesslog_str, "]"); -#else -#ifdef HAVE_GMTIME_R + + if (p->conf.append_tz_offset) { + buffer_append_string_len(p->conf.ts_accesslog_str, tm.tm_gmtoff >= 0 ? "+" : "-", 1); + + scd = abs(tm.tm_gmtoff); + hrs = scd / 3600; + min = (scd % 3600) / 60; + + /* hours */ + if (hrs < 10) buffer_append_string_len(p->conf.ts_accesslog_str, CONST_STR_LEN("0")); + buffer_append_long(p->conf.ts_accesslog_str, hrs); + + if (min < 10) buffer_append_string_len(p->conf.ts_accesslog_str, CONST_STR_LEN("0")); + buffer_append_long(p->conf.ts_accesslog_str, min); + buffer_append_string_len(p->conf.ts_accesslog_str, CONST_STR_LEN("]")); + } +#else /* HAVE_STRUCT_TM_GMTOFF */ +# ifdef HAVE_GMTIME_R gmtime_r(&(srv->cur_ts), &tm); - strftime(p->conf.ts_accesslog_str->ptr, p->conf.ts_accesslog_str->size - 1, "[%d/%b/%Y:%H:%M:%S +0000]", &tm); -#else - strftime(p->conf.ts_accesslog_str->ptr, p->conf.ts_accesslog_str->size - 1, "[%d/%b/%Y:%H:%M:%S +0000]", gmtime(&(srv->cur_ts))); -#endif + strftime(p->conf.ts_accesslog_str->ptr, p->conf.ts_accesslog_str->size - 1, p->conf.ts_accesslog_fmt_str->ptr, &tm); +# else /* HAVE_GMTIME_R */ + strftime(p->conf.ts_accesslog_str->ptr, p->conf.ts_accesslog_str->size - 1, p->conf.ts_accesslog_fmt_str->ptr, gmtime(&(srv->cur_ts))); +# endif /* HAVE_GMTIME_R */ p->conf.ts_accesslog_str->used = strlen(p->conf.ts_accesslog_str->ptr) + 1; -#endif - +#endif /* HAVE_STRUCT_TM_GMTOFF */ + *(p->conf.last_generated_accesslog_ts_ptr) = srv->cur_ts; newts = 1; } - + buffer_append_string_buffer(b, p->conf.ts_accesslog_str); - + break; case FORMAT_REMOTE_HOST: - + /* handle inet_ntop cache */ - + buffer_append_string(b, inet_ntop_cache_get_ip(srv, &(con->dst_addr))); - + break; case FORMAT_REMOTE_IDENT: /* ident */ - BUFFER_APPEND_STRING_CONST(b, "-"); + buffer_append_string_len(b, CONST_STR_LEN("-")); break; case FORMAT_REMOTE_USER: if (con->authed_user->used > 1) { buffer_append_string_buffer(b, con->authed_user); } else { - BUFFER_APPEND_STRING_CONST(b, "-"); + buffer_append_string_len(b, CONST_STR_LEN("-")); } break; case FORMAT_REQUEST_LINE: if (con->request.request_line->used) { - buffer_append_string_buffer(b, con->request.request_line); + accesslog_append_escaped(b, con->request.request_line); } break; case FORMAT_STATUS: buffer_append_long(b, con->http_status); break; - + case FORMAT_BYTES_OUT_NO_HEADER: if (con->bytes_written > 0) { - buffer_append_off_t(b, + buffer_append_off_t(b, con->bytes_written - con->bytes_header <= 0 ? 0 : con->bytes_written - con->bytes_header); } else { - BUFFER_APPEND_STRING_CONST(b, "-"); + buffer_append_string_len(b, CONST_STR_LEN("-")); } break; case FORMAT_HEADER: if (NULL != (ds = (data_string *)array_get_element(con->request.headers, p->conf.parsed_format->ptr[j]->string->ptr))) { - buffer_append_string_buffer(b, ds->value); + accesslog_append_escaped(b, ds->value); } else { - BUFFER_APPEND_STRING_CONST(b, "-"); + buffer_append_string_len(b, CONST_STR_LEN("-")); } break; case FORMAT_RESPONSE_HEADER: if (NULL != (ds = (data_string *)array_get_element(con->response.headers, p->conf.parsed_format->ptr[j]->string->ptr))) { - buffer_append_string_buffer(b, ds->value); + accesslog_append_escaped(b, ds->value); } else { - BUFFER_APPEND_STRING_CONST(b, "-"); + buffer_append_string_len(b, CONST_STR_LEN("-")); + } + break; + case FORMAT_ENV: + if (NULL != (ds = (data_string *)array_get_element(con->environment, p->conf.parsed_format->ptr[j]->string->ptr))) { + accesslog_append_escaped(b, ds->value); + } else { + buffer_append_string_len(b, CONST_STR_LEN("-")); } break; case FORMAT_FILENAME: if (con->physical.path->used > 1) { buffer_append_string_buffer(b, con->physical.path); } else { - BUFFER_APPEND_STRING_CONST(b, "-"); + buffer_append_string_len(b, CONST_STR_LEN("-")); } break; case FORMAT_BYTES_OUT: if (con->bytes_written > 0) { buffer_append_off_t(b, con->bytes_written); } else { - BUFFER_APPEND_STRING_CONST(b, "-"); + buffer_append_string_len(b, CONST_STR_LEN("-")); } break; case FORMAT_BYTES_IN: if (con->bytes_read > 0) { buffer_append_off_t(b, con->bytes_read); } else { - BUFFER_APPEND_STRING_CONST(b, "-"); + buffer_append_string_len(b, CONST_STR_LEN("-")); } break; case FORMAT_TIME_USED: @@ -756,36 +837,52 @@ REQUESTDONE_FUNC(log_access_write) { if (con->server_name->used > 1) { buffer_append_string_buffer(b, con->server_name); } else { - BUFFER_APPEND_STRING_CONST(b, "-"); + buffer_append_string_len(b, CONST_STR_LEN("-")); } break; case FORMAT_HTTP_HOST: if (con->uri.authority->used > 1) { - buffer_append_string_buffer(b, con->uri.authority); + accesslog_append_escaped(b, con->uri.authority); } else { - BUFFER_APPEND_STRING_CONST(b, "-"); + buffer_append_string_len(b, CONST_STR_LEN("-")); } break; case FORMAT_REQUEST_PROTOCOL: - buffer_append_string(b, - con->request.http_version == HTTP_VERSION_1_1 ? "HTTP/1.1" : "HTTP/1.0"); + buffer_append_string_len(b, + con->request.http_version == HTTP_VERSION_1_1 ? "HTTP/1.1" : "HTTP/1.0", 8); break; case FORMAT_REQUEST_METHOD: buffer_append_string(b, get_http_method_name(con->request.http_method)); break; + case FORMAT_PERCENT: + buffer_append_string_len(b, CONST_STR_LEN("%")); + break; case FORMAT_SERVER_PORT: - buffer_append_long(b, srv->srvconf.port); + { + const char *colon; + buffer *srvtoken = ((server_socket*)(con->srv_socket))->srv_token; + if (srvtoken->ptr[0] == '[') { + colon = strstr(srvtoken->ptr, "]:"); + } else { + colon = strchr(srvtoken->ptr, ':'); + } + if (colon) { + buffer_append_string(b, colon+1); + } else { + buffer_append_long(b, srv->srvconf.port); + } + } break; case FORMAT_QUERY_STRING: - buffer_append_string_buffer(b, con->uri.query); + accesslog_append_escaped(b, con->uri.query); break; case FORMAT_URL: - buffer_append_string_buffer(b, con->uri.path_raw); + accesslog_append_escaped(b, con->uri.path_raw); break; case FORMAT_CONNECTION_STATUS: switch(con->keep_alive) { - case 0: buffer_append_string(b, "-"); break; - default: buffer_append_string(b, "+"); break; + case 0: buffer_append_string_len(b, CONST_STR_LEN("-")); break; + default: buffer_append_string_len(b, CONST_STR_LEN("+")); break; } break; default: @@ -794,9 +891,8 @@ REQUESTDONE_FUNC(log_access_write) { { 'A', FORMAT_LOCAL_ADDR }, { 'C', FORMAT_COOKIE }, { 'D', FORMAT_TIME_USED_MS }, - { 'e', FORMAT_ENV }, */ - + break; } break; @@ -804,39 +900,43 @@ REQUESTDONE_FUNC(log_access_write) { break; } } - - BUFFER_APPEND_STRING_CONST(b, "\n"); + + buffer_append_string_len(b, CONST_STR_LEN("\n")); if (p->conf.use_syslog || /* syslog doesn't cache */ - (p->conf.access_logfile->used && p->conf.access_logfile->ptr[0] != '|') || /* pipes don't cache */ + (p->conf.access_logfile->used && p->conf.access_logfile->ptr[0] == '|') || /* pipes don't cache */ newts || b->used > BUFFER_MAX_REUSE_SIZE) { if (p->conf.use_syslog) { #ifdef HAVE_SYSLOG_H - syslog(LOG_INFO, "%*s", b->used - 1, b->ptr); + if (b->used > 2) { + /* syslog appends a \n on its own */ + syslog(LOG_INFO, "%*s", (int) b->used - 2, b->ptr); + } #endif } else if (p->conf.log_access_fd != -1) { write(p->conf.log_access_fd, b->ptr, b->used - 1); } buffer_reset(b); } - + return HANDLER_GO_ON; } +int mod_accesslog_plugin_init(plugin *p); int mod_accesslog_plugin_init(plugin *p) { p->version = LIGHTTPD_VERSION_ID; p->name = buffer_init_string("accesslog"); - + p->init = mod_accesslog_init; p->set_defaults= log_access_open; p->cleanup = mod_accesslog_free; - + p->handle_request_done = log_access_write; p->handle_sighup = log_access_cycle; - + p->data = NULL; - + return 0; } diff --git a/src/mod_alias.c b/src/mod_alias.c index 23570e4..5b7b510 100644 --- a/src/mod_alias.c +++ b/src/mod_alias.c @@ -1,14 +1,14 @@ -#include <ctype.h> -#include <stdlib.h> -#include <string.h> -#include <stdio.h> - #include "base.h" #include "log.h" #include "buffer.h" #include "plugin.h" +#include <ctype.h> +#include <stdlib.h> +#include <string.h> +#include <stdio.h> + /* plugin config for all request/connections */ typedef struct { array *alias; @@ -16,44 +16,46 @@ typedef struct { typedef struct { PLUGIN_DATA; - + plugin_config **config_storage; - - plugin_config conf; + + plugin_config conf; } plugin_data; /* init the plugin data */ INIT_FUNC(mod_alias_init) { plugin_data *p; - + p = calloc(1, sizeof(*p)); - - - + + + return p; } /* detroy the plugin data */ FREE_FUNC(mod_alias_free) { plugin_data *p = p_d; - + if (!p) return HANDLER_GO_ON; - + if (p->config_storage) { size_t i; - + for (i = 0; i < srv->config_context->used; i++) { plugin_config *s = p->config_storage[i]; - + + if(!s) continue; + array_free(s->alias); - + free(s); } free(p->config_storage); } - + free(p); - + return HANDLER_GO_ON; } @@ -62,25 +64,25 @@ FREE_FUNC(mod_alias_free) { SETDEFAULTS_FUNC(mod_alias_set_defaults) { plugin_data *p = p_d; size_t i = 0; - - config_values_t cv[] = { + + config_values_t cv[] = { { "alias.url", NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION }, /* 0 */ { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET } }; - + if (!p) return HANDLER_ERROR; - + p->config_storage = calloc(1, srv->config_context->used * sizeof(specific_config *)); - + for (i = 0; i < srv->config_context->used; i++) { plugin_config *s; - + s = calloc(1, sizeof(plugin_config)); - s->alias = array_init(); + s->alias = array_init(); cv[0].destination = s->alias; - + p->config_storage[i] = s; - + if (0 != config_insert_values_global(srv, ((data_config *)srv->config_context->data[i])->value, cv)) { return HANDLER_ERROR; } @@ -101,16 +103,15 @@ SETDEFAULTS_FUNC(mod_alias_set_defaults) { } /* ok, they have same prefix. check position */ if (a->sorted[j] < a->sorted[k]) { - fprintf(stderr, "url.alias: `%s' will never match as `%s' matched first\n", - key->ptr, - prefix->ptr); + log_error_write(srv, __FILE__, __LINE__, "SBSBS", + "url.alias: `", key, "' will never match as `", prefix, "' matched first"); return HANDLER_ERROR; } } } } } - + return HANDLER_GO_ON; } @@ -119,27 +120,27 @@ SETDEFAULTS_FUNC(mod_alias_set_defaults) { static int mod_alias_patch_connection(server *srv, connection *con, plugin_data *p) { size_t i, j; plugin_config *s = p->config_storage[0]; - + PATCH(alias); - + /* skip the first, the global context */ for (i = 1; i < srv->config_context->used; i++) { data_config *dc = (data_config *)srv->config_context->data[i]; s = p->config_storage[i]; - + /* condition didn't match */ if (!config_check_cond(srv, con, dc)) continue; - + /* merge config */ for (j = 0; j < dc->value->used; j++) { data_unset *du = dc->value->data[j]; - + if (buffer_is_equal_string(du->key, CONST_STR_LEN("alias.url"))) { PATCH(alias); } } } - + return 0; } #undef PATCH @@ -149,51 +150,54 @@ PHYSICALPATH_FUNC(mod_alias_physical_handler) { int uri_len, basedir_len; char *uri_ptr; size_t k; - + if (con->physical.path->used == 0) return HANDLER_GO_ON; - + mod_alias_patch_connection(srv, con, p); - + /* not to include the tailing slash */ basedir_len = (con->physical.basedir->used - 1) - 1; uri_len = con->physical.path->used - 1 - basedir_len; uri_ptr = con->physical.path->ptr + basedir_len; - + for (k = 0; k < p->conf.alias->used; k++) { data_string *ds = (data_string *)p->conf.alias->data[k]; int alias_len = ds->key->used - 1; - + if (alias_len > uri_len) continue; if (ds->key->used == 0) continue; - - if (0 == strncmp(uri_ptr, ds->key->ptr, alias_len)) { + + if (0 == (con->conf.force_lowercase_filenames ? + strncasecmp(uri_ptr, ds->key->ptr, alias_len) : + strncmp(uri_ptr, ds->key->ptr, alias_len))) { /* matched */ - + buffer_copy_string_buffer(con->physical.basedir, ds->value); buffer_copy_string_buffer(srv->tmp_buf, ds->value); buffer_append_string(srv->tmp_buf, uri_ptr + alias_len); buffer_copy_string_buffer(con->physical.path, srv->tmp_buf); - + return HANDLER_GO_ON; } } - + /* not found */ return HANDLER_GO_ON; } /* this function is called at dlopen() time and inits the callbacks */ +int mod_alias_plugin_init(plugin *p); int mod_alias_plugin_init(plugin *p) { p->version = LIGHTTPD_VERSION_ID; p->name = buffer_init_string("alias"); - + p->init = mod_alias_init; p->handle_physical= mod_alias_physical_handler; p->set_defaults = mod_alias_set_defaults; p->cleanup = mod_alias_free; - + p->data = NULL; - + return 0; } diff --git a/src/mod_auth.c b/src/mod_auth.c index 9b791d4..d981892 100644 --- a/src/mod_auth.c +++ b/src/mod_auth.c @@ -1,3 +1,8 @@ +#include "plugin.h" +#include "http_auth.h" +#include "log.h" +#include "response.h" + #include <sys/types.h> #include <sys/stat.h> @@ -7,89 +12,84 @@ #include <fcntl.h> #include <unistd.h> -#include "plugin.h" -#include "http_auth.h" -#include "log.h" -#include "response.h" - handler_t auth_ldap_init(server *srv, mod_auth_plugin_config *s); /** * the basic and digest auth framework - * + * * - config handling * - protocol handling - * - * http_auth.c - * http_auth_digest.c - * + * + * http_auth.c + * http_auth_digest.c + * * do the real work */ INIT_FUNC(mod_auth_init) { mod_auth_plugin_data *p; - + p = calloc(1, sizeof(*p)); - + p->tmp_buf = buffer_init(); - + p->auth_user = buffer_init(); #ifdef USE_LDAP p->ldap_filter = buffer_init(); #endif - + return p; } FREE_FUNC(mod_auth_free) { mod_auth_plugin_data *p = p_d; - + UNUSED(srv); if (!p) return HANDLER_GO_ON; - + buffer_free(p->tmp_buf); buffer_free(p->auth_user); #ifdef USE_LDAP buffer_free(p->ldap_filter); #endif - + if (p->config_storage) { size_t i; for (i = 0; i < srv->config_context->used; i++) { mod_auth_plugin_config *s = p->config_storage[i]; - + if (!s) continue; - + array_free(s->auth_require); buffer_free(s->auth_plain_groupfile); buffer_free(s->auth_plain_userfile); buffer_free(s->auth_htdigest_userfile); buffer_free(s->auth_htpasswd_userfile); buffer_free(s->auth_backend_conf); - + buffer_free(s->auth_ldap_hostname); buffer_free(s->auth_ldap_basedn); buffer_free(s->auth_ldap_binddn); buffer_free(s->auth_ldap_bindpw); buffer_free(s->auth_ldap_filter); buffer_free(s->auth_ldap_cafile); - + #ifdef USE_LDAP buffer_free(s->ldap_filter_pre); buffer_free(s->ldap_filter_post); - + if (s->ldap) ldap_unbind_s(s->ldap); #endif - + free(s); } free(p->config_storage); } - + free(p); - + return HANDLER_GO_ON; } @@ -113,24 +113,25 @@ static int mod_auth_patch_connection(server *srv, connection *con, mod_auth_plug PATCH(auth_ldap_filter); PATCH(auth_ldap_cafile); PATCH(auth_ldap_starttls); + PATCH(auth_ldap_allow_empty_pw); #ifdef USE_LDAP - PATCH(ldap); + p->anon_conf = s; PATCH(ldap_filter_pre); PATCH(ldap_filter_post); #endif - + /* skip the first, the global context */ for (i = 1; i < srv->config_context->used; i++) { data_config *dc = (data_config *)srv->config_context->data[i]; s = p->config_storage[i]; - + /* condition didn't match */ if (!config_check_cond(srv, con, dc)) continue; - + /* merge config */ for (j = 0; j < dc->value->used; j++) { data_unset *du = dc->value->data[j]; - + if (buffer_is_equal_string(du->key, CONST_STR_LEN("auth.backend"))) { PATCH(auth_backend); } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("auth.backend.plain.groupfile"))) { @@ -148,22 +149,30 @@ static int mod_auth_patch_connection(server *srv, connection *con, mod_auth_plug } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("auth.backend.ldap.hostname"))) { PATCH(auth_ldap_hostname); #ifdef USE_LDAP - PATCH(ldap); - PATCH(ldap_filter_pre); - PATCH(ldap_filter_post); + p->anon_conf = s; #endif } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("auth.backend.ldap.base-dn"))) { PATCH(auth_ldap_basedn); } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("auth.backend.ldap.filter"))) { PATCH(auth_ldap_filter); +#ifdef USE_LDAP + PATCH(ldap_filter_pre); + PATCH(ldap_filter_post); +#endif } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("auth.backend.ldap.ca-file"))) { PATCH(auth_ldap_cafile); } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("auth.backend.ldap.starttls"))) { PATCH(auth_ldap_starttls); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("auth.backend.ldap.bind-dn"))) { + PATCH(auth_ldap_binddn); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("auth.backend.ldap.bind-pw"))) { + PATCH(auth_ldap_bindpw); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("auth.backend.ldap.allow-empty-pw"))) { + PATCH(auth_ldap_allow_empty_pw); } } } - + return 0; } #undef PATCH @@ -172,116 +181,121 @@ static handler_t mod_auth_uri_handler(server *srv, connection *con, void *p_d) { size_t k; int auth_required = 0, auth_satisfied = 0; char *http_authorization = NULL; + const char *auth_type = NULL; data_string *ds; mod_auth_plugin_data *p = p_d; array *req; - + /* select the right config */ mod_auth_patch_connection(srv, con, p); - + if (p->conf.auth_require == NULL) return HANDLER_GO_ON; - + /* * AUTH - * + * */ - + /* do we have to ask for auth ? */ - + auth_required = 0; auth_satisfied = 0; - + /* search auth-directives for path */ for (k = 0; k < p->conf.auth_require->used; k++) { - buffer *req = p->conf.auth_require->data[k]->key; + buffer *require = p->conf.auth_require->data[k]->key; - if (req->used == 0) continue; - if (con->uri.path->used < req->used) continue; + if (require->used == 0) continue; + if (con->uri.path->used < require->used) continue; /* if we have a case-insensitive FS we have to lower-case the URI here too */ if (con->conf.force_lowercase_filenames) { - if (0 == strncasecmp(con->uri.path->ptr, req->ptr, req->used - 1)) { + if (0 == strncasecmp(con->uri.path->ptr, require->ptr, require->used - 1)) { auth_required = 1; break; } } else { - if (0 == strncmp(con->uri.path->ptr, req->ptr, req->used - 1)) { + if (0 == strncmp(con->uri.path->ptr, require->ptr, require->used - 1)) { auth_required = 1; break; } } } - + /* nothing to do for us */ if (auth_required == 0) return HANDLER_GO_ON; - + req = ((data_array *)(p->conf.auth_require->data[k]))->value; - + /* try to get Authorization-header */ - + if (NULL != (ds = (data_string *)array_get_element(con->request.headers, "Authorization"))) { http_authorization = ds->value->ptr; } - + if (ds && ds->value && ds->value->used) { char *auth_realm; data_string *method; - + method = (data_string *)array_get_element(req, "method"); - + /* parse auth-header */ if (NULL != (auth_realm = strchr(http_authorization, ' '))) { int auth_type_len = auth_realm - http_authorization; - + if ((auth_type_len == 5) && - (0 == strncmp(http_authorization, "Basic", auth_type_len))) { - + (0 == strncasecmp(http_authorization, "Basic", auth_type_len))) { + auth_type = "Basic"; + if (0 == strcmp(method->value->ptr, "basic")) { auth_satisfied = http_auth_basic_check(srv, con, p, req, con->uri.path, auth_realm+1); } } else if ((auth_type_len == 6) && - (0 == strncmp(http_authorization, "Digest", auth_type_len))) { + (0 == strncasecmp(http_authorization, "Digest", auth_type_len))) { + auth_type = "Digest"; if (0 == strcmp(method->value->ptr, "digest")) { if (-1 == (auth_satisfied = http_auth_digest_check(srv, con, p, req, con->uri.path, auth_realm+1))) { con->http_status = 400; - + con->mode = DIRECT; + /* a field was missing */ - + return HANDLER_FINISHED; } } } else { - log_error_write(srv, __FILE__, __LINE__, "ss", + log_error_write(srv, __FILE__, __LINE__, "ss", "unknown authentification type:", http_authorization); } } } - + if (!auth_satisfied) { data_string *method, *realm; method = (data_string *)array_get_element(req, "method"); realm = (data_string *)array_get_element(req, "realm"); - + con->http_status = 401; - + con->mode = DIRECT; + if (0 == strcmp(method->value->ptr, "basic")) { - buffer_copy_string(p->tmp_buf, "Basic realm=\""); + buffer_copy_string_len(p->tmp_buf, CONST_STR_LEN("Basic realm=\"")); buffer_append_string_buffer(p->tmp_buf, realm->value); - buffer_append_string(p->tmp_buf, "\""); - + buffer_append_string_len(p->tmp_buf, CONST_STR_LEN("\"")); + response_header_insert(srv, con, CONST_STR_LEN("WWW-Authenticate"), CONST_BUF_LEN(p->tmp_buf)); } else if (0 == strcmp(method->value->ptr, "digest")) { char hh[33]; http_auth_digest_generate_nonce(srv, p, srv->tmp_buf, hh); - - buffer_copy_string(p->tmp_buf, "Digest realm=\""); + + buffer_copy_string_len(p->tmp_buf, CONST_STR_LEN("Digest realm=\"")); buffer_append_string_buffer(p->tmp_buf, realm->value); - buffer_append_string(p->tmp_buf, "\", nonce=\""); + buffer_append_string_len(p->tmp_buf, CONST_STR_LEN("\", nonce=\"")); buffer_append_string(p->tmp_buf, hh); - buffer_append_string(p->tmp_buf, "\", qop=\"auth\""); - + buffer_append_string_len(p->tmp_buf, CONST_STR_LEN("\", qop=\"auth\"")); + response_header_insert(srv, con, CONST_STR_LEN("WWW-Authenticate"), CONST_BUF_LEN(p->tmp_buf)); } else { /* evil */ @@ -289,35 +303,47 @@ static handler_t mod_auth_uri_handler(server *srv, connection *con, void *p_d) { return HANDLER_FINISHED; } else { /* the REMOTE_USER header */ - + buffer_copy_string_buffer(con->authed_user, p->auth_user); + + /* AUTH_TYPE environment */ + + if (NULL == (ds = (data_string *)array_get_unused_element(con->environment, TYPE_STRING))) { + ds = data_string_init(); + } + + buffer_copy_string(ds->key, "AUTH_TYPE"); + buffer_copy_string(ds->value, auth_type); + + array_insert_unique(con->environment, (data_unset *)ds); } - + return HANDLER_GO_ON; } SETDEFAULTS_FUNC(mod_auth_set_defaults) { mod_auth_plugin_data *p = p_d; size_t i; - - config_values_t cv[] = { + + config_values_t cv[] = { { "auth.backend", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 0 */ - { "auth.backend.plain.groupfile", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, - { "auth.backend.plain.userfile", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, - { "auth.require", NULL, T_CONFIG_LOCAL, T_CONFIG_SCOPE_CONNECTION }, - { "auth.backend.ldap.hostname", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, - { "auth.backend.ldap.base-dn", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, - { "auth.backend.ldap.filter", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, - { "auth.backend.ldap.ca-file", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, - { "auth.backend.ldap.starttls", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, - { "auth.backend.ldap.bind-dn", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, + { "auth.backend.plain.groupfile", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 1 */ + { "auth.backend.plain.userfile", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 2 */ + { "auth.require", NULL, T_CONFIG_LOCAL, T_CONFIG_SCOPE_CONNECTION }, /* 3 */ + { "auth.backend.ldap.hostname", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 4 */ + { "auth.backend.ldap.base-dn", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 5 */ + { "auth.backend.ldap.filter", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 6 */ + { "auth.backend.ldap.ca-file", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 7 */ + { "auth.backend.ldap.starttls", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 8 */ + { "auth.backend.ldap.bind-dn", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 9 */ { "auth.backend.ldap.bind-pw", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 10 */ - { "auth.backend.htdigest.userfile", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, - { "auth.backend.htpasswd.userfile", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, - { "auth.debug", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 13 */ + { "auth.backend.ldap.allow-empty-pw", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 11 */ + { "auth.backend.htdigest.userfile", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 12 */ + { "auth.backend.htpasswd.userfile", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 13 */ + { "auth.debug", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 14 */ { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET } }; - + p->config_storage = calloc(1, srv->config_context->used * sizeof(specific_config *)); for (i = 0; i < srv->config_context->used; i++) { @@ -325,14 +351,14 @@ SETDEFAULTS_FUNC(mod_auth_set_defaults) { size_t n; data_array *da; array *ca; - + s = calloc(1, sizeof(mod_auth_plugin_config)); s->auth_plain_groupfile = buffer_init(); s->auth_plain_userfile = buffer_init(); s->auth_htdigest_userfile = buffer_init(); s->auth_htpasswd_userfile = buffer_init(); s->auth_backend_conf = buffer_init(); - + s->auth_ldap_hostname = buffer_init(); s->auth_ldap_basedn = buffer_init(); s->auth_ldap_binddn = buffer_init(); @@ -341,15 +367,15 @@ SETDEFAULTS_FUNC(mod_auth_set_defaults) { s->auth_ldap_cafile = buffer_init(); s->auth_ldap_starttls = 0; s->auth_debug = 0; - + s->auth_require = array_init(); - + #ifdef USE_LDAP s->ldap_filter_pre = buffer_init(); s->ldap_filter_post = buffer_init(); s->ldap = NULL; #endif - + cv[0].destination = s->auth_backend_conf; cv[1].destination = s->auth_plain_groupfile; cv[2].destination = s->auth_plain_userfile; @@ -359,19 +385,20 @@ SETDEFAULTS_FUNC(mod_auth_set_defaults) { cv[6].destination = s->auth_ldap_filter; cv[7].destination = s->auth_ldap_cafile; cv[8].destination = &(s->auth_ldap_starttls); - cv[9].destination = s->auth_ldap_binddn; - cv[10].destination = s->auth_ldap_bindpw; - cv[11].destination = s->auth_htdigest_userfile; - cv[12].destination = s->auth_htpasswd_userfile; - cv[13].destination = &(s->auth_debug); - + cv[9].destination = s->auth_ldap_binddn; + cv[10].destination = s->auth_ldap_bindpw; + cv[11].destination = &(s->auth_ldap_allow_empty_pw); + cv[12].destination = s->auth_htdigest_userfile; + cv[13].destination = s->auth_htpasswd_userfile; + cv[14].destination = &(s->auth_debug); + p->config_storage[i] = s; ca = ((data_config *)srv->config_context->data[i])->value; - + if (0 != config_insert_values_global(srv, ca, cv)) { return HANDLER_ERROR; } - + if (s->auth_backend_conf->used) { if (0 == strcmp(s->auth_backend_conf->ptr, "htpasswd")) { s->auth_backend = AUTH_BACKEND_HTPASSWD; @@ -383,30 +410,48 @@ SETDEFAULTS_FUNC(mod_auth_set_defaults) { s->auth_backend = AUTH_BACKEND_LDAP; } else { log_error_write(srv, __FILE__, __LINE__, "sb", "auth.backend not supported:", s->auth_backend_conf); - + return HANDLER_ERROR; } } +#ifdef USE_LDAP + if (s->auth_ldap_filter->used) { + char *dollar; + + /* parse filter */ + + if (NULL == (dollar = strchr(s->auth_ldap_filter->ptr, '$'))) { + log_error_write(srv, __FILE__, __LINE__, "s", "ldap: auth.backend.ldap.filter is missing a replace-operator '$'"); + + return HANDLER_ERROR; + } + + buffer_copy_string_len(s->ldap_filter_pre, s->auth_ldap_filter->ptr, dollar - s->auth_ldap_filter->ptr); + buffer_copy_string(s->ldap_filter_post, dollar+1); + } +#endif + /* no auth.require for this section */ if (NULL == (da = (data_array *)array_get_element(ca, "auth.require"))) continue; - + if (da->type != TYPE_ARRAY) continue; - + for (n = 0; n < da->value->used; n++) { size_t m; data_array *da_file = (data_array *)da->value->data[n]; const char *method, *realm, *require; - + if (da->value->data[n]->type != TYPE_ARRAY) { - log_error_write(srv, __FILE__, __LINE__, "sssbs", - "unexpected type for key: ", "auth.require", "[", da->value->data[n]->key, "](string)"); - + log_error_write(srv, __FILE__, __LINE__, "ss", + "auth.require should contain an array as in:", + "auth.require = ( \"...\" => ( ..., ...) )"); + return HANDLER_ERROR; } - + method = realm = require = NULL; - + for (m = 0; m < da_file->value->used; m++) { if (da_file->value->data[m]->type == TYPE_STRING) { if (0 == strcmp(da_file->value->data[m]->key->ptr, "method")) { @@ -416,206 +461,174 @@ SETDEFAULTS_FUNC(mod_auth_set_defaults) { } else if (0 == strcmp(da_file->value->data[m]->key->ptr, "require")) { require = ((data_string *)(da_file->value->data[m]))->value->ptr; } else { - log_error_write(srv, __FILE__, __LINE__, "sssbs", "unexpected type for key: ", "auth.require", "[", da_file->value->data[m]->key, "](string)"); + log_error_write(srv, __FILE__, __LINE__, "ssbs", + "the field is unknown in:", + "auth.require = ( \"...\" => ( ..., -> \"", + da_file->value->data[m]->key, + "\" <- => \"...\" ) )"); + return HANDLER_ERROR; } } else { - log_error_write(srv, __FILE__, __LINE__, "sssbs", "unexpected type for key: ", "auth.require", "[", da_file->value->data[m]->key, "](string)"); - + log_error_write(srv, __FILE__, __LINE__, "ssbs", + "a string was expected for:", + "auth.require = ( \"...\" => ( ..., -> \"", + da_file->value->data[m]->key, + "\" <- => \"...\" ) )"); + return HANDLER_ERROR; } } - + if (method == NULL) { - log_error_write(srv, __FILE__, __LINE__, "sssss", "missing entry for key: ", "auth.require", "[", "method", "](string)"); + log_error_write(srv, __FILE__, __LINE__, "ss", + "the method field is missing in:", + "auth.require = ( \"...\" => ( ..., \"method\" => \"...\" ) )"); return HANDLER_ERROR; } else { if (0 != strcmp(method, "basic") && 0 != strcmp(method, "digest")) { - log_error_write(srv, __FILE__, __LINE__, "s", "auth.require->method has to be either 'basic' or 'digest'"); + log_error_write(srv, __FILE__, __LINE__, "ss", + "method has to be either \"basic\" or \"digest\" in", + "auth.require = ( \"...\" => ( ..., \"method\" => \"...\") )"); return HANDLER_ERROR; } } - + if (realm == NULL) { - log_error_write(srv, __FILE__, __LINE__, "sssss", "missing entry for key: ", "auth.require", "[", "realm", "](string)"); + log_error_write(srv, __FILE__, __LINE__, "ss", + "the realm field is missing in:", + "auth.require = ( \"...\" => ( ..., \"realm\" => \"...\" ) )"); return HANDLER_ERROR; } - + if (require == NULL) { - log_error_write(srv, __FILE__, __LINE__, "sssss", "missing entry for key: ", "auth.require", "[", "require", "](string)"); + log_error_write(srv, __FILE__, __LINE__, "ss", + "the require field is missing in:", + "auth.require = ( \"...\" => ( ..., \"require\" => \"...\" ) )"); return HANDLER_ERROR; } - + if (method && realm && require) { data_string *ds; data_array *a; - + a = data_array_init(); buffer_copy_string_buffer(a->key, da_file->key); - + ds = data_string_init(); - - buffer_copy_string(ds->key, "method"); + + buffer_copy_string_len(ds->key, CONST_STR_LEN("method")); buffer_copy_string(ds->value, method); - + array_insert_unique(a->value, (data_unset *)ds); - + ds = data_string_init(); - - buffer_copy_string(ds->key, "realm"); + + buffer_copy_string_len(ds->key, CONST_STR_LEN("realm")); buffer_copy_string(ds->value, realm); - + array_insert_unique(a->value, (data_unset *)ds); - + ds = data_string_init(); - - buffer_copy_string(ds->key, "require"); + + buffer_copy_string_len(ds->key, CONST_STR_LEN("require")); buffer_copy_string(ds->value, require); - + array_insert_unique(a->value, (data_unset *)ds); - + array_insert_unique(s->auth_require, (data_unset *)a); } } - - switch(s->auth_backend) { - case AUTH_BACKEND_PLAIN: - if (s->auth_plain_userfile->used) { - int fd; - /* try to read */ - if (-1 == (fd = open(s->auth_plain_userfile->ptr, O_RDONLY))) { - log_error_write(srv, __FILE__, __LINE__, "sbss", - "opening auth.backend.plain.userfile:", s->auth_plain_userfile, - "failed:", strerror(errno)); - return HANDLER_ERROR; - } - close(fd); - } - break; - case AUTH_BACKEND_HTPASSWD: - if (s->auth_htpasswd_userfile->used) { - int fd; - /* try to read */ - if (-1 == (fd = open(s->auth_htpasswd_userfile->ptr, O_RDONLY))) { - log_error_write(srv, __FILE__, __LINE__, "sbss", - "opening auth.backend.htpasswd.userfile:", s->auth_htpasswd_userfile, - "failed:", strerror(errno)); - return HANDLER_ERROR; - } - close(fd); - } - break; - case AUTH_BACKEND_HTDIGEST: - if (s->auth_htdigest_userfile->used) { - int fd; - /* try to read */ - if (-1 == (fd = open(s->auth_htdigest_userfile->ptr, O_RDONLY))) { - log_error_write(srv, __FILE__, __LINE__, "sbss", - "opening auth.backend.htdigest.userfile:", s->auth_htdigest_userfile, - "failed:", strerror(errno)); - return HANDLER_ERROR; - } - close(fd); - } - break; + + switch(s->auth_ldap_hostname->used) { case AUTH_BACKEND_LDAP: { handler_t ret = auth_ldap_init(srv, s); if (ret == HANDLER_ERROR) return (ret); - break; + break; } - default: - break; - } - } + default: + break; + } + } - return HANDLER_GO_ON; + return HANDLER_GO_ON; } handler_t auth_ldap_init(server *srv, mod_auth_plugin_config *s) { #ifdef USE_LDAP - int ret; -#if 0 - if (s->auth_ldap_basedn->used == 0) { - log_error_write(srv, __FILE__, __LINE__, "s", "ldap: auth.backend.ldap.base-dn has to be set"); - - return HANDLER_ERROR; - } + int ret; +#if 0 + if (s->auth_ldap_basedn->used == 0) { + log_error_write(srv, __FILE__, __LINE__, "s", "ldap: auth.backend.ldap.base-dn has to be set"); + + return HANDLER_ERROR; + } #endif - - if (s->auth_ldap_filter->used) { - char *dollar; - - /* parse filter */ - - if (NULL == (dollar = strchr(s->auth_ldap_filter->ptr, '$'))) { - log_error_write(srv, __FILE__, __LINE__, "s", "ldap: auth.backend.ldap.filter is missing a replace-operator '$'"); - + + if (s->auth_ldap_hostname->used) { + /* free old context */ + if (NULL != s->ldap) ldap_unbind_s(s->ldap); + + if (NULL == (s->ldap = ldap_init(s->auth_ldap_hostname->ptr, LDAP_PORT))) { + log_error_write(srv, __FILE__, __LINE__, "ss", "ldap ...", strerror(errno)); + + return HANDLER_ERROR; + } + + ret = LDAP_VERSION3; + if (LDAP_OPT_SUCCESS != (ret = ldap_set_option(s->ldap, LDAP_OPT_PROTOCOL_VERSION, &ret))) { + log_error_write(srv, __FILE__, __LINE__, "ss", "ldap:", ldap_err2string(ret)); + + return HANDLER_ERROR; + } + + if (s->auth_ldap_starttls) { + /* if no CA file is given, it is ok, as we will use encryption + * if the server requires a CAfile it will tell us */ + if (!buffer_is_empty(s->auth_ldap_cafile)) { + if (LDAP_OPT_SUCCESS != (ret = ldap_set_option(NULL, LDAP_OPT_X_TLS_CACERTFILE, + s->auth_ldap_cafile->ptr))) { + log_error_write(srv, __FILE__, __LINE__, "ss", + "Loading CA certificate failed:", ldap_err2string(ret)); + return HANDLER_ERROR; } - - buffer_copy_string_len(s->ldap_filter_pre, s->auth_ldap_filter->ptr, dollar - s->auth_ldap_filter->ptr); - buffer_copy_string(s->ldap_filter_post, dollar+1); } - - if (s->auth_ldap_hostname->used) { - if (NULL == (s->ldap = ldap_init(s->auth_ldap_hostname->ptr, LDAP_PORT))) { - log_error_write(srv, __FILE__, __LINE__, "ss", "ldap ...", strerror(errno)); - - return HANDLER_ERROR; - } - - ret = LDAP_VERSION3; - if (LDAP_OPT_SUCCESS != (ret = ldap_set_option(s->ldap, LDAP_OPT_PROTOCOL_VERSION, &ret))) { - log_error_write(srv, __FILE__, __LINE__, "ss", "ldap:", ldap_err2string(ret)); - - return HANDLER_ERROR; - } - if (s->auth_ldap_starttls) { - /* if no CA file is given, it is ok, as we will use encryption - * if the server requires a CAfile it will tell us */ - if (!buffer_is_empty(s->auth_ldap_cafile)) { - if (LDAP_OPT_SUCCESS != (ret = ldap_set_option(NULL, LDAP_OPT_X_TLS_CACERTFILE, - s->auth_ldap_cafile->ptr))) { - log_error_write(srv, __FILE__, __LINE__, "ss", - "Loading CA certificate failed:", ldap_err2string(ret)); - - return HANDLER_ERROR; - } - } - - if (LDAP_OPT_SUCCESS != (ret = ldap_start_tls_s(s->ldap, NULL, NULL))) { - log_error_write(srv, __FILE__, __LINE__, "ss", "ldap startTLS failed:", ldap_err2string(ret)); - - return HANDLER_ERROR; - } - } - - - /* 1. */ - if (s->auth_ldap_binddn->used) { - if (LDAP_SUCCESS != (ret = ldap_simple_bind_s(s->ldap, s->auth_ldap_binddn->ptr, s->auth_ldap_bindpw->ptr))) { - log_error_write(srv, __FILE__, __LINE__, "ss", "ldap:", ldap_err2string(ret)); - - return HANDLER_ERROR; - } - } else { - if (LDAP_SUCCESS != (ret = ldap_simple_bind_s(s->ldap, NULL, NULL))) { - log_error_write(srv, __FILE__, __LINE__, "ss", "ldap:", ldap_err2string(ret)); - - return HANDLER_ERROR; - } - } + if (LDAP_OPT_SUCCESS != (ret = ldap_start_tls_s(s->ldap, NULL, NULL))) { + log_error_write(srv, __FILE__, __LINE__, "ss", "ldap startTLS failed:", ldap_err2string(ret)); + + return HANDLER_ERROR; + } + } + + + /* 1. */ + if (s->auth_ldap_binddn->used) { + if (LDAP_SUCCESS != (ret = ldap_simple_bind_s(s->ldap, s->auth_ldap_binddn->ptr, s->auth_ldap_bindpw->ptr))) { + log_error_write(srv, __FILE__, __LINE__, "ss", "ldap:", ldap_err2string(ret)); + + return HANDLER_ERROR; } + } else { + if (LDAP_SUCCESS != (ret = ldap_simple_bind_s(s->ldap, NULL, NULL))) { + log_error_write(srv, __FILE__, __LINE__, "ss", "ldap:", ldap_err2string(ret)); + + return HANDLER_ERROR; + } + } + } + return HANDLER_GO_ON; #else - log_error_write(srv, __FILE__, __LINE__, "s", "no ldap support available"); - return HANDLER_ERROR; + UNUSED(s); + log_error_write(srv, __FILE__, __LINE__, "s", "no ldap support available"); + return HANDLER_ERROR; #endif - return HANDLER_GO_ON; } +int mod_auth_plugin_init(plugin *p); int mod_auth_plugin_init(plugin *p) { p->version = LIGHTTPD_VERSION_ID; p->name = buffer_init_string("auth"); @@ -623,8 +636,8 @@ int mod_auth_plugin_init(plugin *p) { p->set_defaults = mod_auth_set_defaults; p->handle_uri_clean = mod_auth_uri_handler; p->cleanup = mod_auth_free; - + p->data = NULL; - + return 0; } diff --git a/src/mod_cgi.c b/src/mod_cgi.c index 9480032..5e65f4b 100644 --- a/src/mod_cgi.c +++ b/src/mod_cgi.c @@ -1,14 +1,23 @@ +#include "server.h" +#include "stat_cache.h" +#include "keyvalue.h" +#include "log.h" +#include "connections.h" +#include "joblist.h" +#include "http_chunk.h" + +#include "plugin.h" + #include <sys/types.h> + #ifdef __WIN32 -#include <winsock2.h> +# include <winsock2.h> #else -#include <sys/socket.h> -#include <sys/wait.h> -#include <sys/mman.h> - -#include <netinet/in.h> - -#include <arpa/inet.h> +# include <sys/socket.h> +# include <sys/wait.h> +# include <sys/mman.h> +# include <netinet/in.h> +# include <arpa/inet.h> #endif #include <unistd.h> @@ -23,24 +32,17 @@ #include <stdio.h> #include <fcntl.h> -#include "server.h" -#include "keyvalue.h" -#include "log.h" -#include "connections.h" -#include "joblist.h" -#include "http_chunk.h" - -#include "plugin.h" - #ifdef HAVE_SYS_FILIO_H # include <sys/filio.h> #endif +#include "version.h" + enum {EOL_UNSET, EOL_N, EOL_RN}; typedef struct { char **ptr; - + size_t size; size_t used; } char_array; @@ -53,47 +55,48 @@ typedef struct { typedef struct { array *cgi; + unsigned short execute_x_only; } plugin_config; typedef struct { PLUGIN_DATA; buffer_pid_t cgi_pid; - + buffer *tmp_buf; buffer *parse_response; - + plugin_config **config_storage; - - plugin_config conf; + + plugin_config conf; } plugin_data; typedef struct { pid_t pid; int fd; int fde_ndx; /* index into the fd-event buffer */ - + connection *remote_conn; /* dumb pointer */ plugin_data *plugin_data; /* dumb pointer */ - + buffer *response; buffer *response_header; } handler_ctx; -static handler_ctx * cgi_handler_ctx_init() { +static handler_ctx * cgi_handler_ctx_init(void) { handler_ctx *hctx = calloc(1, sizeof(*hctx)); assert(hctx); - + hctx->response = buffer_init(); hctx->response_header = buffer_init(); - + return hctx; } static void cgi_handler_ctx_free(handler_ctx *hctx) { buffer_free(hctx->response); buffer_free(hctx->response_header); - + free(hctx); } @@ -101,14 +104,14 @@ enum {FDEVENT_HANDLED_UNSET, FDEVENT_HANDLED_FINISHED, FDEVENT_HANDLED_NOT_FINIS INIT_FUNC(mod_cgi_init) { plugin_data *p; - + p = calloc(1, sizeof(*p)); assert(p); - + p->tmp_buf = buffer_init(); p->parse_response = buffer_init(); - + return p; } @@ -116,62 +119,65 @@ INIT_FUNC(mod_cgi_init) { FREE_FUNC(mod_cgi_free) { plugin_data *p = p_d; buffer_pid_t *r = &(p->cgi_pid); - + UNUSED(srv); - + if (p->config_storage) { size_t i; for (i = 0; i < srv->config_context->used; i++) { plugin_config *s = p->config_storage[i]; - + array_free(s->cgi); - + free(s); } free(p->config_storage); } - + if (r->ptr) free(r->ptr); - + buffer_free(p->tmp_buf); buffer_free(p->parse_response); - + free(p); - + return HANDLER_GO_ON; } SETDEFAULTS_FUNC(mod_fastcgi_set_defaults) { plugin_data *p = p_d; size_t i = 0; - - config_values_t cv[] = { + + config_values_t cv[] = { { "cgi.assign", NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION }, /* 0 */ + { "cgi.execute-x-only", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 1 */ { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET} }; if (!p) return HANDLER_ERROR; - + p->config_storage = calloc(1, srv->config_context->used * sizeof(specific_config *)); - + for (i = 0; i < srv->config_context->used; i++) { plugin_config *s; - + s = calloc(1, sizeof(plugin_config)); assert(s); - + s->cgi = array_init(); - + s->execute_x_only = 0; + cv[0].destination = s->cgi; - + cv[1].destination = &(s->execute_x_only); + p->config_storage[i] = s; - + if (0 != config_insert_values_global(srv, ((data_config *)srv->config_context->data[i])->value, cv)) { return HANDLER_ERROR; } } - + return HANDLER_GO_ON; } @@ -180,13 +186,13 @@ static int cgi_pid_add(server *srv, plugin_data *p, pid_t pid) { int m = -1; size_t i; buffer_pid_t *r = &(p->cgi_pid); - + UNUSED(srv); for (i = 0; i < r->used; i++) { if (r->ptr[i] > m) m = r->ptr[i]; } - + if (r->size == 0) { r->size = 16; r->ptr = malloc(sizeof(*r->ptr) * r->size); @@ -194,93 +200,96 @@ static int cgi_pid_add(server *srv, plugin_data *p, pid_t pid) { r->size += 16; r->ptr = realloc(r->ptr, sizeof(*r->ptr) * r->size); } - + r->ptr[r->used++] = pid; - + return m; } static int cgi_pid_del(server *srv, plugin_data *p, pid_t pid) { size_t i; buffer_pid_t *r = &(p->cgi_pid); - + UNUSED(srv); for (i = 0; i < r->used; i++) { if (r->ptr[i] == pid) break; } - + if (i != r->used) { /* found */ - + if (i != r->used - 1) { r->ptr[i] = r->ptr[r->used - 1]; } r->used--; } - + return 0; } -static int cgi_response_parse(server *srv, connection *con, plugin_data *p, buffer *in, int eol) { +static int cgi_response_parse(server *srv, connection *con, plugin_data *p, buffer *in) { char *ns; const char *s; int line = 0; - + UNUSED(srv); - + buffer_copy_string_buffer(p->parse_response, in); - - for (s = p->parse_response->ptr; - NULL != (ns = (eol == EOL_RN ? strstr(s, "\r\n") : strchr(s, '\n'))); - s = ns + (eol == EOL_RN ? 2 : 1), line++) { + + for (s = p->parse_response->ptr; + NULL != (ns = strchr(s, '\n')); + s = ns + 1, line++) { const char *key, *value; int key_len; data_string *ds; - + + /* strip the \n */ ns[0] = '\0'; - - if (line == 0 && + + if (ns > s && ns[-1] == '\r') ns[-1] = '\0'; + + if (line == 0 && 0 == strncmp(s, "HTTP/1.", 7)) { /* non-parsed header ... we parse them anyway */ - + if ((s[7] == '1' || s[7] == '0') && s[8] == ' ') { int status; /* after the space should be a status code for us */ - + status = strtol(s+9, NULL, 10); - - if (con->http_status >= 100 && - con->http_status < 1000) { + + if (status >= 100 && + status < 1000) { /* we expected 3 digits and didn't got them */ con->parsed_response |= HTTP_STATUS; con->http_status = status; } } } else { - + /* parse the headers */ key = s; if (NULL == (value = strchr(s, ':'))) { /* we expect: "<key>: <value>\r\n" */ continue; } - + key_len = value - key; value += 1; - + /* skip LWS */ while (*value == ' ' || *value == '\t') value++; - + if (NULL == (ds = (data_string *)array_get_unused_element(con->response.headers, TYPE_STRING))) { ds = data_response_init(); } buffer_copy_string_len(ds->key, key, key_len); buffer_copy_string(ds->value, value); - + array_insert_unique(con->response.headers, (data_unset *)ds); - + switch(key_len) { case 4: if (0 == strncasecmp(key, "Date", key_len)) { @@ -315,13 +324,13 @@ static int cgi_response_parse(server *srv, connection *con, plugin_data *p, buff } } } - + /* CGI/1.1 rev 03 - 7.2.1.2 */ if ((con->parsed_response & HTTP_LOCATION) && !(con->parsed_response & HTTP_STATUS)) { con->http_status = 302; } - + return 0; } @@ -329,11 +338,22 @@ static int cgi_response_parse(server *srv, connection *con, plugin_data *p, buff static int cgi_demux_response(server *srv, handler_ctx *hctx) { plugin_data *p = hctx->plugin_data; connection *con = hctx->remote_conn; - + while(1) { int n; - - buffer_prepare_copy(hctx->response, 1024); + int toread; + +#if defined(__WIN32) + buffer_prepare_copy(hctx->response, 4 * 1024); +#else + if (ioctl(con->fd, FIONREAD, &toread) || toread == 0 || toread <= 4*1024) { + buffer_prepare_copy(hctx->response, 4 * 1024); + } else { + if (toread > MAX_READ_LIMIT) toread = MAX_READ_LIMIT; + buffer_prepare_copy(hctx->response, toread + 1); + } +#endif + if (-1 == (n = read(hctx->fd, hctx->response->ptr, hctx->response->size - 1))) { if (errno == EAGAIN || errno == EINTR) { /* would block, wait for signal */ @@ -343,125 +363,153 @@ static int cgi_demux_response(server *srv, handler_ctx *hctx) { log_error_write(srv, __FILE__, __LINE__, "sdd", strerror(errno), con->fd, hctx->fd); return FDEVENT_HANDLED_ERROR; } - + if (n == 0) { /* read finished */ - + con->file_finished = 1; - + /* send final chunk */ http_chunk_append_mem(srv, con, NULL, 0); joblist_append(srv, con); - + return FDEVENT_HANDLED_FINISHED; } - + hctx->response->ptr[n] = '\0'; hctx->response->used = n+1; - + /* split header from body */ - + if (con->file_started == 0) { - char *c; - int in_header = 0; - int header_end = 0; - int cp, eol = EOL_UNSET; - size_t used = 0; - + int is_header = 0; + int is_header_end = 0; + size_t last_eol = 0; + size_t i; + buffer_append_string_buffer(hctx->response_header, hctx->response); + + /** + * we have to handle a few cases: + * + * nph: + * + * HTTP/1.0 200 Ok\n + * Header: Value\n + * \n + * + * CGI: + * Header: Value\n + * Status: 200\n + * \n + * + * and different mixes of \n and \r\n combinations + * + * Some users also forget about CGI and just send a response and hope + * we handle it. No headers, no header-content seperator + * + */ /* nph (non-parsed headers) */ - if (0 == strncmp(hctx->response_header->ptr, "HTTP/1.", 7)) in_header = 1; - - /* search for the \r\n\r\n or \n\n in the string */ - for (c = hctx->response_header->ptr, cp = 0, used = hctx->response_header->used - 1; used; c++, cp++, used--) { - if (*c == ':') in_header = 1; - else if (*c == '\n') { - if (in_header == 0) { - /* got a response without a response header */ - - c = NULL; - header_end = 1; - break; - } - - if (eol == EOL_UNSET) eol = EOL_N; - - if (*(c+1) == '\n') { - header_end = 1; - break; - } - - } else if (used > 1 && *c == '\r' && *(c+1) == '\n') { - if (in_header == 0) { - /* got a response without a response header */ - - c = NULL; - header_end = 1; + if (0 == strncmp(hctx->response_header->ptr, "HTTP/1.", 7)) is_header = 1; + + for (i = 0; !is_header_end && i < hctx->response_header->used - 1; i++) { + char c = hctx->response_header->ptr[i]; + + switch (c) { + case ':': + /* we found a colon + * + * looks like we have a normal header + */ + is_header = 1; + break; + case '\n': + /* EOL */ + if (is_header == 0) { + /* we got a EOL but we don't seem to got a HTTP header */ + + is_header_end = 1; + break; } - - if (eol == EOL_UNSET) eol = EOL_RN; - - if (used > 3 && - *(c+2) == '\r' && - *(c+3) == '\n') { - header_end = 1; + + /** + * check if we saw a \n(\r)?\n sequence + */ + if (last_eol > 0 && + ((i - last_eol == 1) || + (i - last_eol == 2 && hctx->response_header->ptr[i - 1] == '\r'))) { + is_header_end = 1; break; } - - /* skip the \n */ - c++; - cp++; - used--; + + last_eol = i; + + break; } } - - if (header_end) { - if (c == NULL) { + + if (is_header_end) { + if (!is_header) { /* no header, but a body */ - + if (con->request.http_version == HTTP_VERSION_1_1) { con->response.transfer_encoding = HTTP_TRANSFER_ENCODING_CHUNKED; } - + http_chunk_append_mem(srv, con, hctx->response_header->ptr, hctx->response_header->used); joblist_append(srv, con); } else { - size_t hlen = c - hctx->response_header->ptr + (eol == EOL_RN ? 4 : 2); - size_t blen = hctx->response_header->used - hlen - 1; - - /* a small hack: terminate after at the second \r */ - hctx->response_header->used = hlen + 1 - (eol == EOL_RN ? 2 : 1); - hctx->response_header->ptr[hlen - (eol == EOL_RN ? 2 : 1)] = '\0'; - - /* parse the response header */ - cgi_response_parse(srv, con, p, hctx->response_header, eol); + const char *bstart; + size_t blen; + + /** + * i still points to the char after the terminating EOL EOL + * + * put it on the last \n again + */ + i--; + + /* the body starts after the EOL */ + bstart = hctx->response_header->ptr + (i + 1); + blen = (hctx->response_header->used - 1) - (i + 1); + + /* string the last \r?\n */ + if (i > 0 && (hctx->response_header->ptr[i - 1] == '\r')) { + i--; + } + + hctx->response_header->ptr[i] = '\0'; + hctx->response_header->used = i + 1; /* the string + \0 */ + /* parse the response header */ + cgi_response_parse(srv, con, p, hctx->response_header); + /* enable chunked-transfer-encoding */ if (con->request.http_version == HTTP_VERSION_1_1 && !(con->parsed_response & HTTP_CONTENT_LENGTH)) { con->response.transfer_encoding = HTTP_TRANSFER_ENCODING_CHUNKED; } - - if ((hctx->response->used != hlen) && blen > 0) { - http_chunk_append_mem(srv, con, c + (eol == EOL_RN ? 4: 2), blen + 1); + + if (blen > 0) { + http_chunk_append_mem(srv, con, bstart, blen + 1); joblist_append(srv, con); } } - + con->file_started = 1; } } else { http_chunk_append_mem(srv, con, hctx->response->ptr, hctx->response->used); joblist_append(srv, con); } - -#if 0 + +#if 0 log_error_write(srv, __FILE__, __LINE__, "ddss", con->fd, hctx->fd, connection_get_state(con->state), b->ptr); #endif } - + return FDEVENT_HANDLED_NOT_FINISHED; } @@ -470,42 +518,42 @@ static handler_t cgi_connection_close(server *srv, handler_ctx *hctx) { pid_t pid; plugin_data *p; connection *con; - + if (NULL == hctx) return HANDLER_GO_ON; - + p = hctx->plugin_data; con = hctx->remote_conn; - + if (con->mode != p->id) return HANDLER_GO_ON; #ifndef __WIN32 - + /* the connection to the browser went away, but we still have a connection - * to the CGI script + * to the CGI script * * close cgi-connection */ - + if (hctx->fd != -1) { /* close connection to the cgi-script */ fdevent_event_del(srv->ev, &(hctx->fde_ndx), hctx->fd); fdevent_unregister(srv->ev, hctx->fd); - + if (close(hctx->fd)) { log_error_write(srv, __FILE__, __LINE__, "sds", "cgi close failed ", hctx->fd, strerror(errno)); } - + hctx->fd = -1; hctx->fde_ndx = -1; } - + pid = hctx->pid; - + con->plugin_ctx[p->id] = NULL; - + /* is this a good idea ? */ cgi_handler_ctx_free(hctx); - + /* if waitpid hasn't been called by response.c yet, do it here */ if (pid) { /* check if the CGI-script is already gone */ @@ -519,19 +567,19 @@ static handler_t cgi_connection_close(server *srv, handler_ctx *hctx) { case -1: /* */ if (errno == EINTR) break; - - /* - * errno == ECHILD happens if _subrequest catches the process-status before + + /* + * errno == ECHILD happens if _subrequest catches the process-status before * we have read the response of the cgi process - * + * * -> catch status * -> WAIT_FOR_EVENT * -> read response * -> we get here with waitpid == ECHILD - * + * */ - if (errno == ECHILD) return HANDLER_FINISHED; - + if (errno == ECHILD) return HANDLER_GO_ON; + log_error_write(srv, __FILE__, __LINE__, "ss", "waitpid failed: ", strerror(errno)); return HANDLER_ERROR; default: @@ -540,80 +588,83 @@ static handler_t cgi_connection_close(server *srv, handler_ctx *hctx) { connection_set_state(srv, con, CON_STATE_HANDLE_REQUEST); con->http_status = 500; con->mode = DIRECT; + } else { + con->file_finished = 1; } - + if (WIFEXITED(status)) { #if 0 log_error_write(srv, __FILE__, __LINE__, "sd", "(debug) cgi exited fine, pid:", pid); #endif - pid = 0; - - return HANDLER_FINISHED; + return HANDLER_GO_ON; } else { log_error_write(srv, __FILE__, __LINE__, "sd", "cgi died, pid:", pid); - pid = 0; - return HANDLER_FINISHED; + return HANDLER_GO_ON; } } - - + + kill(pid, SIGTERM); - + /* cgi-script is still alive, queue the PID for removal */ cgi_pid_add(srv, p, pid); } -#endif - return HANDLER_FINISHED; +#endif + return HANDLER_GO_ON; } static handler_t cgi_connection_close_callback(server *srv, connection *con, void *p_d) { plugin_data *p = p_d; - + return cgi_connection_close(srv, con->plugin_ctx[p->id]); } -static handler_t cgi_handle_fdevent(void *s, void *ctx, int revents) { - server *srv = (server *)s; +static handler_t cgi_handle_fdevent(server *srv, void *ctx, int revents) { handler_ctx *hctx = ctx; connection *con = hctx->remote_conn; - + joblist_append(srv, con); - + if (hctx->fd == -1) { log_error_write(srv, __FILE__, __LINE__, "ddss", con->fd, hctx->fd, connection_get_state(con->state), "invalid cgi-fd"); - + return HANDLER_ERROR; } - + if (revents & FDEVENT_IN) { switch (cgi_demux_response(srv, hctx)) { case FDEVENT_HANDLED_NOT_FINISHED: break; case FDEVENT_HANDLED_FINISHED: /* we are done */ - + #if 0 log_error_write(srv, __FILE__, __LINE__, "ddss", con->fd, hctx->fd, connection_get_state(con->state), "finished"); #endif cgi_connection_close(srv, hctx); - - /* if we get a IN|HUP and have read everything don't exec the close twice */ + + /* if we get a IN|HUP and have read everything don't exec the close twice */ return HANDLER_FINISHED; case FDEVENT_HANDLED_ERROR: - connection_set_state(srv, con, CON_STATE_HANDLE_REQUEST); - con->http_status = 500; - con->mode = DIRECT; - + /* Send an error if we haven't sent any data yet */ + if (0 == con->file_started) { + connection_set_state(srv, con, CON_STATE_HANDLE_REQUEST); + con->http_status = 500; + con->mode = DIRECT; + } else { + con->file_finished = 1; + } + log_error_write(srv, __FILE__, __LINE__, "s", "demuxer failed: "); break; } } - + if (revents & FDEVENT_OUT) { /* nothing to do */ } - + /* perhaps this issue is already handled */ if (revents & FDEVENT_HUP) { /* check if we still have a unfinished header package which is a body in reality */ @@ -623,57 +674,54 @@ static handler_t cgi_handle_fdevent(void *s, void *ctx, int revents) { http_chunk_append_mem(srv, con, hctx->response_header->ptr, hctx->response_header->used); joblist_append(srv, con); } - + if (con->file_finished == 0) { http_chunk_append_mem(srv, con, NULL, 0); joblist_append(srv, con); } - + con->file_finished = 1; - + if (chunkqueue_is_empty(con->write_queue)) { /* there is nothing left to write */ connection_set_state(srv, con, CON_STATE_RESPONSE_END); } else { /* used the write-handler to finish the request on demand */ - + } - + # if 0 log_error_write(srv, __FILE__, __LINE__, "sddd", "got HUP from cgi", con->fd, hctx->fd, revents); # endif - + /* rtsigs didn't liked the close */ cgi_connection_close(srv, hctx); } else if (revents & FDEVENT_ERR) { con->file_finished = 1; - + /* kill all connections to the cgi process */ cgi_connection_close(srv, hctx); #if 1 log_error_write(srv, __FILE__, __LINE__, "s", "cgi-FDEVENT_ERR"); -#endif +#endif return HANDLER_ERROR; } - + return HANDLER_FINISHED; } -static int cgi_env_add(char_array *env, const char *key, size_t key_len, const char *val) { - int val_len; +static int cgi_env_add(char_array *env, const char *key, size_t key_len, const char *val, size_t val_len) { char *dst; - + if (!key || !val) return -1; - - val_len = strlen(val); - - dst = malloc(key_len + val_len + 3); + + dst = malloc(key_len + val_len + 2); memcpy(dst, key, key_len); dst[key_len] = '='; - /* add the \0 from the value */ - memcpy(dst + key_len + 1, val, val_len + 1); - + memcpy(dst + key_len + 1, val, val_len); + dst[key_len + 1 + val_len] = '\0'; + if (env->size == 0) { env->size = 16; env->ptr = malloc(env->size * sizeof(*env->ptr)); @@ -681,45 +729,47 @@ static int cgi_env_add(char_array *env, const char *key, size_t key_len, const c env->size += 16; env->ptr = realloc(env->ptr, env->size * sizeof(*env->ptr)); } - + env->ptr[env->used++] = dst; - + return 0; } static int cgi_create_env(server *srv, connection *con, plugin_data *p, buffer *cgi_handler) { pid_t pid; - + #ifdef HAVE_IPV6 char b2[INET6_ADDRSTRLEN + 1]; #endif - + int to_cgi_fds[2]; int from_cgi_fds[2]; struct stat st; - -#ifndef __WIN32 - + +#ifndef __WIN32 + if (cgi_handler->used > 1) { /* stat the exec file */ if (-1 == (stat(cgi_handler->ptr, &st))) { - log_error_write(srv, __FILE__, __LINE__, "sbss", + log_error_write(srv, __FILE__, __LINE__, "sbss", "stat for cgi-handler", cgi_handler, "failed:", strerror(errno)); return -1; } } - + if (pipe(to_cgi_fds)) { log_error_write(srv, __FILE__, __LINE__, "ss", "pipe failed:", strerror(errno)); return -1; } - + if (pipe(from_cgi_fds)) { + close(to_cgi_fds[0]); + close(to_cgi_fds[1]); log_error_write(srv, __FILE__, __LINE__, "ss", "pipe failed:", strerror(errno)); return -1; } - + /* fork, execve */ switch (pid = fork()) { case 0: { @@ -731,142 +781,193 @@ static int cgi_create_env(server *srv, connection *con, plugin_data *p, buffer * size_t n; char_array env; char *c; + const char *s; server_socket *srv_sock = con->srv_socket; - + /* move stdout to from_cgi_fd[1] */ close(STDOUT_FILENO); dup2(from_cgi_fds[1], STDOUT_FILENO); close(from_cgi_fds[1]); /* not needed */ close(from_cgi_fds[0]); - + /* move the stdin to to_cgi_fd[0] */ close(STDIN_FILENO); dup2(to_cgi_fds[0], STDIN_FILENO); close(to_cgi_fds[0]); /* not needed */ close(to_cgi_fds[1]); - - /* HACK: - * this is not nice, but it works - * - * we feed the stderr of the CGI to our errorlog, if possible - */ - if (srv->errorlog_mode == ERRORLOG_FILE) { - close(STDERR_FILENO); - dup2(srv->errorlog_fd, STDERR_FILENO); - } - + /* create environment */ env.ptr = NULL; env.size = 0; env.used = 0; - - cgi_env_add(&env, CONST_STR_LEN("SERVER_SOFTWARE"), PACKAGE_NAME"/"PACKAGE_VERSION); - cgi_env_add(&env, CONST_STR_LEN("SERVER_NAME"), - con->server_name->used ? - con->server_name->ptr : + + if (buffer_is_empty(con->conf.server_tag)) { + cgi_env_add(&env, CONST_STR_LEN("SERVER_SOFTWARE"), CONST_STR_LEN(PACKAGE_DESC)); + } else { + cgi_env_add(&env, CONST_STR_LEN("SERVER_SOFTWARE"), CONST_BUF_LEN(con->conf.server_tag)); + } + + if (!buffer_is_empty(con->server_name)) { + size_t len = con->server_name->used - 1; + + if (con->server_name->ptr[0] == '[') { + const char *colon = strstr(con->server_name->ptr, "]:"); + if (colon) len = (colon + 1) - con->server_name->ptr; + } else { + const char *colon = strchr(con->server_name->ptr, ':'); + if (colon) len = colon - con->server_name->ptr; + } + + cgi_env_add(&env, CONST_STR_LEN("SERVER_NAME"), con->server_name->ptr, len); + } else { #ifdef HAVE_IPV6 - inet_ntop(srv_sock->addr.plain.sa_family, - srv_sock->addr.plain.sa_family == AF_INET6 ? + s = inet_ntop(srv_sock->addr.plain.sa_family, + srv_sock->addr.plain.sa_family == AF_INET6 ? (const void *) &(srv_sock->addr.ipv6.sin6_addr) : (const void *) &(srv_sock->addr.ipv4.sin_addr), - b2, sizeof(b2)-1) + b2, sizeof(b2)-1); #else - inet_ntoa(srv_sock->addr.ipv4.sin_addr) + s = inet_ntoa(srv_sock->addr.ipv4.sin_addr); #endif - ); - cgi_env_add(&env, CONST_STR_LEN("GATEWAY_INTERFACE"), "CGI/1.1"); - - cgi_env_add(&env, CONST_STR_LEN("SERVER_PROTOCOL"), get_http_version_name(con->request.http_version)); - - ltostr(buf, + cgi_env_add(&env, CONST_STR_LEN("SERVER_NAME"), s, strlen(s)); + } + cgi_env_add(&env, CONST_STR_LEN("GATEWAY_INTERFACE"), CONST_STR_LEN("CGI/1.1")); + + s = get_http_version_name(con->request.http_version); + + cgi_env_add(&env, CONST_STR_LEN("SERVER_PROTOCOL"), s, strlen(s)); + + LI_ltostr(buf, #ifdef HAVE_IPV6 ntohs(srv_sock->addr.plain.sa_family == AF_INET6 ? srv_sock->addr.ipv6.sin6_port : srv_sock->addr.ipv4.sin_port) #else ntohs(srv_sock->addr.ipv4.sin_port) #endif ); - cgi_env_add(&env, CONST_STR_LEN("SERVER_PORT"), buf); - - cgi_env_add(&env, CONST_STR_LEN("SERVER_ADDR"), + cgi_env_add(&env, CONST_STR_LEN("SERVER_PORT"), buf, strlen(buf)); + + switch (srv_sock->addr.plain.sa_family) { #ifdef HAVE_IPV6 - inet_ntop(srv_sock->addr.plain.sa_family, - srv_sock->addr.plain.sa_family == AF_INET6 ? - (const void *) &(srv_sock->addr.ipv6.sin6_addr) : - (const void *) &(srv_sock->addr.ipv4.sin_addr), - b2, sizeof(b2)-1) + case AF_INET6: + s = inet_ntop(srv_sock->addr.plain.sa_family, + (const void *) &(srv_sock->addr.ipv6.sin6_addr), + b2, sizeof(b2)-1); + break; + case AF_INET: + s = inet_ntop(srv_sock->addr.plain.sa_family, + (const void *) &(srv_sock->addr.ipv4.sin_addr), + b2, sizeof(b2)-1); + break; #else - inet_ntoa(srv_sock->addr.ipv4.sin_addr) + case AF_INET: + s = inet_ntoa(srv_sock->addr.ipv4.sin_addr); + break; #endif - ); - - cgi_env_add(&env, CONST_STR_LEN("REQUEST_METHOD"), get_http_method_name(con->request.http_method)); - if (con->request.pathinfo->used) { - cgi_env_add(&env, CONST_STR_LEN("PATH_INFO"), con->request.pathinfo->ptr); + default: + s = ""; + break; } - cgi_env_add(&env, CONST_STR_LEN("REDIRECT_STATUS"), "200"); - cgi_env_add(&env, CONST_STR_LEN("QUERY_STRING"), con->uri.query->used ? con->uri.query->ptr : ""); - cgi_env_add(&env, CONST_STR_LEN("REQUEST_URI"), con->request.orig_uri->used ? con->request.orig_uri->ptr : ""); - - - cgi_env_add(&env, CONST_STR_LEN("REMOTE_ADDR"), + cgi_env_add(&env, CONST_STR_LEN("SERVER_ADDR"), s, strlen(s)); + + s = get_http_method_name(con->request.http_method); + cgi_env_add(&env, CONST_STR_LEN("REQUEST_METHOD"), s, strlen(s)); + + if (!buffer_is_empty(con->request.pathinfo)) { + cgi_env_add(&env, CONST_STR_LEN("PATH_INFO"), CONST_BUF_LEN(con->request.pathinfo)); + } + cgi_env_add(&env, CONST_STR_LEN("REDIRECT_STATUS"), CONST_STR_LEN("200")); + if (!buffer_is_empty(con->uri.query)) { + cgi_env_add(&env, CONST_STR_LEN("QUERY_STRING"), CONST_BUF_LEN(con->uri.query)); + } + if (!buffer_is_empty(con->request.orig_uri)) { + cgi_env_add(&env, CONST_STR_LEN("REQUEST_URI"), CONST_BUF_LEN(con->request.orig_uri)); + } + + + switch (con->dst_addr.plain.sa_family) { #ifdef HAVE_IPV6 - inet_ntop(con->dst_addr.plain.sa_family, - con->dst_addr.plain.sa_family == AF_INET6 ? - (const void *) &(con->dst_addr.ipv6.sin6_addr) : - (const void *) &(con->dst_addr.ipv4.sin_addr), - b2, sizeof(b2)-1) + case AF_INET6: + s = inet_ntop(con->dst_addr.plain.sa_family, + (const void *) &(con->dst_addr.ipv6.sin6_addr), + b2, sizeof(b2)-1); + break; + case AF_INET: + s = inet_ntop(con->dst_addr.plain.sa_family, + (const void *) &(con->dst_addr.ipv4.sin_addr), + b2, sizeof(b2)-1); + break; #else - inet_ntoa(con->dst_addr.ipv4.sin_addr) + case AF_INET: + s = inet_ntoa(con->dst_addr.ipv4.sin_addr); + break; #endif - ); + default: + s = ""; + break; + } + cgi_env_add(&env, CONST_STR_LEN("REMOTE_ADDR"), s, strlen(s)); - ltostr(buf, + LI_ltostr(buf, #ifdef HAVE_IPV6 ntohs(con->dst_addr.plain.sa_family == AF_INET6 ? con->dst_addr.ipv6.sin6_port : con->dst_addr.ipv4.sin_port) #else ntohs(con->dst_addr.ipv4.sin_port) #endif ); - cgi_env_add(&env, CONST_STR_LEN("REMOTE_PORT"), buf); - - if (con->authed_user->used) { + cgi_env_add(&env, CONST_STR_LEN("REMOTE_PORT"), buf, strlen(buf)); + + if (!buffer_is_empty(con->authed_user)) { cgi_env_add(&env, CONST_STR_LEN("REMOTE_USER"), - con->authed_user->ptr); + CONST_BUF_LEN(con->authed_user)); } - + +#ifdef USE_OPENSSL + if (srv_sock->is_ssl) { + cgi_env_add(&env, CONST_STR_LEN("HTTPS"), CONST_STR_LEN("on")); + } +#endif + /* request.content_length < SSIZE_MAX, see request.c */ - ltostr(buf, con->request.content_length); - cgi_env_add(&env, CONST_STR_LEN("CONTENT_LENGTH"), buf); - cgi_env_add(&env, CONST_STR_LEN("SCRIPT_FILENAME"), con->physical.path->ptr); - cgi_env_add(&env, CONST_STR_LEN("SCRIPT_NAME"), con->uri.path->ptr); - + LI_ltostr(buf, con->request.content_length); + cgi_env_add(&env, CONST_STR_LEN("CONTENT_LENGTH"), buf, strlen(buf)); + cgi_env_add(&env, CONST_STR_LEN("SCRIPT_FILENAME"), CONST_BUF_LEN(con->physical.path)); + cgi_env_add(&env, CONST_STR_LEN("SCRIPT_NAME"), CONST_BUF_LEN(con->uri.path)); + cgi_env_add(&env, CONST_STR_LEN("DOCUMENT_ROOT"), CONST_BUF_LEN(con->physical.basedir)); + /* for valgrind */ - cgi_env_add(&env, CONST_STR_LEN("LD_PRELOAD"), getenv("LD_PRELOAD")); - cgi_env_add(&env, CONST_STR_LEN("LD_LIBRARY_PATH"), getenv("LD_LIBRARY_PATH")); + if (NULL != (s = getenv("LD_PRELOAD"))) { + cgi_env_add(&env, CONST_STR_LEN("LD_PRELOAD"), s, strlen(s)); + } + + if (NULL != (s = getenv("LD_LIBRARY_PATH"))) { + cgi_env_add(&env, CONST_STR_LEN("LD_LIBRARY_PATH"), s, strlen(s)); + } #ifdef __CYGWIN__ /* CYGWIN needs SYSTEMROOT */ - cgi_env_add(&env, CONST_STR_LEN("SYSTEMROOT"), getenv("SYSTEMROOT")); + if (NULL != (s = getenv("SYSTEMROOT"))) { + cgi_env_add(&env, CONST_STR_LEN("SYSTEMROOT"), s, strlen(s)); + } #endif - + for (n = 0; n < con->request.headers->used; n++) { data_string *ds; - + ds = (data_string *)con->request.headers->data[n]; - + if (ds->value->used && ds->key->used) { size_t j; - + buffer_reset(p->tmp_buf); - + if (0 != strcasecmp(ds->key->ptr, "CONTENT-TYPE")) { - buffer_copy_string(p->tmp_buf, "HTTP_"); + buffer_copy_string_len(p->tmp_buf, CONST_STR_LEN("HTTP_")); p->tmp_buf->used--; /* strip \0 after HTTP_ */ } - + buffer_prepare_append(p->tmp_buf, ds->key->used + 2); - + for (j = 0; j < ds->key->used - 1; j++) { char cr = '_'; if (light_isalpha(ds->key->ptr[j])) { @@ -879,56 +980,62 @@ static int cgi_create_env(server *srv, connection *con, plugin_data *p, buffer * p->tmp_buf->ptr[p->tmp_buf->used++] = cr; } p->tmp_buf->ptr[p->tmp_buf->used++] = '\0'; - - cgi_env_add(&env, CONST_BUF_LEN(p->tmp_buf), ds->value->ptr); + + cgi_env_add(&env, CONST_BUF_LEN(p->tmp_buf), CONST_BUF_LEN(ds->value)); } } - + for (n = 0; n < con->environment->used; n++) { data_string *ds; - + ds = (data_string *)con->environment->data[n]; - + if (ds->value->used && ds->key->used) { size_t j; - + buffer_reset(p->tmp_buf); - + buffer_prepare_append(p->tmp_buf, ds->key->used + 2); - + for (j = 0; j < ds->key->used - 1; j++) { - p->tmp_buf->ptr[p->tmp_buf->used++] = - isalpha((unsigned char)ds->key->ptr[j]) ? - toupper((unsigned char)ds->key->ptr[j]) : '_'; + char cr = '_'; + if (light_isalpha(ds->key->ptr[j])) { + /* upper-case */ + cr = ds->key->ptr[j] & ~32; + } else if (light_isdigit(ds->key->ptr[j])) { + /* copy */ + cr = ds->key->ptr[j]; + } + p->tmp_buf->ptr[p->tmp_buf->used++] = cr; } p->tmp_buf->ptr[p->tmp_buf->used++] = '\0'; - - cgi_env_add(&env, CONST_BUF_LEN(p->tmp_buf), ds->value->ptr); + + cgi_env_add(&env, CONST_BUF_LEN(p->tmp_buf), CONST_BUF_LEN(ds->value)); } } - + if (env.size == env.used) { env.size += 16; env.ptr = realloc(env.ptr, env.size * sizeof(*env.ptr)); } - + env.ptr[env.used] = NULL; - + /* set up args */ argc = 3; args = malloc(sizeof(*args) * argc); i = 0; - + if (cgi_handler->used > 1) { args[i++] = cgi_handler->ptr; } args[i++] = con->physical.path->ptr; - args[i++] = NULL; + args[i ] = NULL; /* search for the last / */ if (NULL != (c = strrchr(con->physical.path->ptr, '/'))) { *c = '\0'; - + /* change to the physical directory */ if (-1 == chdir(con->physical.path->ptr)) { log_error_write(srv, __FILE__, __LINE__, "ssb", "chdir failed:", strerror(errno), con->physical.path); @@ -940,12 +1047,12 @@ static int cgi_create_env(server *srv, connection *con, plugin_data *p, buffer * for (i = 3; i < 256; i++) { if (i != srv->errorlog_fd) close(i); } - + /* exec the cgi */ execve(args[0], args, env.ptr); - - log_error_write(srv, __FILE__, __LINE__, "sss", "CGI failed:", strerror(errno), args[0]); - + + /* log_error_write(srv, __FILE__, __LINE__, "sss", "CGI failed:", strerror(errno), args[0]); */ + /* */ SEGFAULT(); break; @@ -953,15 +1060,23 @@ static int cgi_create_env(server *srv, connection *con, plugin_data *p, buffer * case -1: /* error */ log_error_write(srv, __FILE__, __LINE__, "ss", "fork failed:", strerror(errno)); + close(from_cgi_fds[0]); + close(from_cgi_fds[1]); + close(to_cgi_fds[0]); + close(to_cgi_fds[1]); + return -1; break; default: { handler_ctx *hctx; /* father */ - + + close(from_cgi_fds[1]); + close(to_cgi_fds[0]); + if (con->request.content_length) { chunkqueue *cq = con->request_content_queue; chunk *c; - + assert(chunkqueue_length(cq) == (off_t)con->request.content_length); /* there is content to send */ @@ -976,22 +1091,26 @@ static int cgi_create_env(server *srv, connection *con, plugin_data *p, buffer * if (-1 == c->file.fd && /* open the file if not already open */ -1 == (c->file.fd = open(c->file.name->ptr, O_RDONLY))) { log_error_write(srv, __FILE__, __LINE__, "ss", "open failed: ", strerror(errno)); - + + close(from_cgi_fds[0]); + close(to_cgi_fds[1]); return -1; } c->file.mmap.length = c->file.length; - - if (MAP_FAILED == (c->file.mmap.start = mmap(0, c->file.mmap.length, PROT_READ, MAP_SHARED, c->file.fd, 0))) { - log_error_write(srv, __FILE__, __LINE__, "ssbd", "mmap failed: ", + + if (MAP_FAILED == (c->file.mmap.start = mmap(NULL, c->file.mmap.length, PROT_READ, MAP_SHARED, c->file.fd, 0))) { + log_error_write(srv, __FILE__, __LINE__, "ssbd", "mmap failed: ", strerror(errno), c->file.name, c->file.fd); + close(from_cgi_fds[0]); + close(to_cgi_fds[1]); return -1; } close(c->file.fd); c->file.fd = -1; - + /* chunk_reset() or chunk_free() will cleanup for us */ } @@ -999,8 +1118,9 @@ static int cgi_create_env(server *srv, connection *con, plugin_data *p, buffer * switch(errno) { case ENOSPC: con->http_status = 507; - break; + case EINTR: + continue; default: con->http_status = 403; break; @@ -1012,8 +1132,9 @@ static int cgi_create_env(server *srv, connection *con, plugin_data *p, buffer * switch(errno) { case ENOSPC: con->http_status = 507; - break; + case EINTR: + continue; default: con->http_status = 403; break; @@ -1028,55 +1149,54 @@ static int cgi_create_env(server *srv, connection *con, plugin_data *p, buffer * c->offset += r; cq->bytes_out += r; } else { + log_error_write(srv, __FILE__, __LINE__, "ss", "write() failed due to: ", strerror(errno)); + con->http_status = 500; break; } chunkqueue_remove_finished_chunks(cq); } } - - close(from_cgi_fds[1]); - - close(to_cgi_fds[0]); + close(to_cgi_fds[1]); - + /* register PID and wait for them asyncronously */ con->mode = p->id; buffer_reset(con->physical.path); - + hctx = cgi_handler_ctx_init(); - + hctx->remote_conn = con; hctx->plugin_data = p; hctx->pid = pid; hctx->fd = from_cgi_fds[0]; hctx->fde_ndx = -1; - + con->plugin_ctx[p->id] = hctx; - + fdevent_register(srv->ev, hctx->fd, cgi_handle_fdevent, hctx); - fdevent_event_add(srv->ev, &(hctx->fde_ndx), hctx->fd, FDEVENT_IN); - + fdevent_event_set(srv->ev, &(hctx->fde_ndx), hctx->fd, FDEVENT_IN); + if (-1 == fdevent_fcntl_set(srv->ev, hctx->fd)) { log_error_write(srv, __FILE__, __LINE__, "ss", "fcntl failed: ", strerror(errno)); - + fdevent_event_del(srv->ev, &(hctx->fde_ndx), hctx->fd); fdevent_unregister(srv->ev, hctx->fd); - + log_error_write(srv, __FILE__, __LINE__, "sd", "cgi close:", hctx->fd); - + close(hctx->fd); - + cgi_handler_ctx_free(hctx); - + con->plugin_ctx[p->id] = NULL; - + return -1; } - + break; } } - + return 0; #else return -1; @@ -1088,27 +1208,30 @@ static int cgi_create_env(server *srv, connection *con, plugin_data *p, buffer * static int mod_cgi_patch_connection(server *srv, connection *con, plugin_data *p) { size_t i, j; plugin_config *s = p->config_storage[0]; - + PATCH(cgi); - + PATCH(execute_x_only); + /* skip the first, the global context */ for (i = 1; i < srv->config_context->used; i++) { data_config *dc = (data_config *)srv->config_context->data[i]; s = p->config_storage[i]; - + /* condition didn't match */ if (!config_check_cond(srv, con, dc)) continue; - + /* merge config */ for (j = 0; j < dc->value->used; j++) { data_unset *du = dc->value->data[j]; - + if (buffer_is_equal_string(du->key, CONST_STR_LEN("cgi.assign"))) { PATCH(cgi); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("cgi.execute-x-only"))) { + PATCH(execute_x_only); } } } - + return 0; } #undef PATCH @@ -1117,24 +1240,32 @@ URIHANDLER_FUNC(cgi_is_handled) { size_t k, s_len; plugin_data *p = p_d; buffer *fn = con->physical.path; - - if (fn->used == 0) return HANDLER_ERROR; - + stat_cache_entry *sce = NULL; + + if (con->mode != DIRECT) return HANDLER_GO_ON; + + if (fn->used == 0) return HANDLER_GO_ON; + mod_cgi_patch_connection(srv, con, p); - + + if (HANDLER_ERROR == stat_cache_get_entry(srv, con, con->physical.path, &sce)) return HANDLER_GO_ON; + if (!S_ISREG(sce->st.st_mode)) return HANDLER_GO_ON; + if (p->conf.execute_x_only == 1 && (sce->st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) == 0) return HANDLER_GO_ON; + s_len = fn->used - 1; - + for (k = 0; k < p->conf.cgi->used; k++) { data_string *ds = (data_string *)p->conf.cgi->data[k]; size_t ct_len = ds->key->used - 1; - + if (ds->key->used == 0) continue; if (s_len < ct_len) continue; - + if (0 == strncmp(fn->ptr + s_len - ct_len, ds->key->ptr, ct_len)) { if (cgi_create_env(srv, con, p, ds->value)) { + con->mode = DIRECT; con->http_status = 500; - + buffer_reset(con->physical.path); return HANDLER_FINISHED; } @@ -1142,7 +1273,7 @@ URIHANDLER_FUNC(cgi_is_handled) { break; } } - + return HANDLER_GO_ON; } @@ -1154,7 +1285,7 @@ TRIGGER_FUNC(cgi_trigger) { for (ndx = 0; ndx < p->cgi_pid.used; ndx++) { int status; - + switch(waitpid(p->cgi_pid.ptr[ndx], &status, WNOHANG)) { case 0: /* not finished yet */ @@ -1163,8 +1294,17 @@ TRIGGER_FUNC(cgi_trigger) { #endif break; case -1: + if (errno == ECHILD) { + /* someone else called waitpid... remove the pid to stop looping the error each time */ + log_error_write(srv, __FILE__, __LINE__, "s", "cgi child vanished, probably someone else called waitpid"); + + cgi_pid_del(srv, p, p->cgi_pid.ptr[ndx]); + ndx--; + continue; + } + log_error_write(srv, __FILE__, __LINE__, "ss", "waitpid failed: ", strerror(errno)); - + return HANDLER_ERROR; default: @@ -1172,35 +1312,52 @@ TRIGGER_FUNC(cgi_trigger) { #if 0 log_error_write(srv, __FILE__, __LINE__, "sd", "(debug) cgi exited fine, pid:", p->cgi_pid.ptr[ndx]); #endif + } else if (WIFSIGNALED(status)) { + /* FIXME: what if we killed the CGI script with a kill(..., SIGTERM) ? + */ + if (WTERMSIG(status) != SIGTERM) { + log_error_write(srv, __FILE__, __LINE__, "sd", "cleaning up CGI: process died with signal", WTERMSIG(status)); + } } else { - log_error_write(srv, __FILE__, __LINE__, "s", "cgi died ?"); + log_error_write(srv, __FILE__, __LINE__, "s", "cleaning up CGI: ended unexpectedly"); } - + cgi_pid_del(srv, p, p->cgi_pid.ptr[ndx]); - /* del modified the buffer structure + /* del modified the buffer structure * and copies the last entry to the current one * -> recheck the current index */ ndx--; } } -#endif +#endif return HANDLER_GO_ON; } +/* + * - HANDLER_GO_ON : not our job + * - HANDLER_FINISHED: got response header + * - HANDLER_WAIT_FOR_EVENT: waiting for response header + */ SUBREQUEST_FUNC(mod_cgi_handle_subrequest) { int status; plugin_data *p = p_d; handler_ctx *hctx = con->plugin_ctx[p->id]; - + if (con->mode != p->id) return HANDLER_GO_ON; if (NULL == hctx) return HANDLER_GO_ON; - + #if 0 log_error_write(srv, __FILE__, __LINE__, "sdd", "subrequest, pid =", hctx, hctx->pid); -#endif - if (hctx->pid == 0) return HANDLER_FINISHED; -#ifndef __WIN32 +#endif + + if (hctx->pid == 0) { + /* cgi already dead */ + if (!con->file_started) return HANDLER_WAIT_FOR_EVENT; + return HANDLER_FINISHED; + } + +#ifndef __WIN32 switch(waitpid(hctx->pid, &status, WNOHANG)) { case 0: /* we only have for events here if we don't have the header yet, @@ -1210,61 +1367,61 @@ SUBREQUEST_FUNC(mod_cgi_handle_subrequest) { return HANDLER_WAIT_FOR_EVENT; case -1: if (errno == EINTR) return HANDLER_WAIT_FOR_EVENT; - + if (errno == ECHILD && con->file_started == 0) { /* - * second round but still not response + * second round but still not response */ - return HANDLER_WAIT_FOR_EVENT; + return HANDLER_WAIT_FOR_EVENT; } - + log_error_write(srv, __FILE__, __LINE__, "ss", "waitpid failed: ", strerror(errno)); con->mode = DIRECT; con->http_status = 500; - + hctx->pid = 0; - + fdevent_event_del(srv->ev, &(hctx->fde_ndx), hctx->fd); fdevent_unregister(srv->ev, hctx->fd); - + if (close(hctx->fd)) { log_error_write(srv, __FILE__, __LINE__, "sds", "cgi close failed ", hctx->fd, strerror(errno)); } - + cgi_handler_ctx_free(hctx); - + con->plugin_ctx[p->id] = NULL; - + return HANDLER_FINISHED; default: - /* cgi process exited cleanly - * - * check if we already got the response + /* cgi process exited */ - - if (!con->file_started) return HANDLER_WAIT_FOR_EVENT; - + + hctx->pid = 0; + + /* we already have response headers? just continue */ + if (con->file_started) return HANDLER_FINISHED; + if (WIFEXITED(status)) { - /* nothing */ - } else { - log_error_write(srv, __FILE__, __LINE__, "s", "cgi died ?"); - - con->mode = DIRECT; - con->http_status = 500; - + /* clean exit - just continue */ + return HANDLER_WAIT_FOR_EVENT; } - - hctx->pid = 0; - + + /* cgi proc died, and we didn't get any data yet - send error message and close cgi con */ + log_error_write(srv, __FILE__, __LINE__, "s", "cgi died ?"); + + con->http_status = 500; + con->mode = DIRECT; + fdevent_event_del(srv->ev, &(hctx->fde_ndx), hctx->fd); fdevent_unregister(srv->ev, hctx->fd); - + if (close(hctx->fd)) { log_error_write(srv, __FILE__, __LINE__, "sds", "cgi close failed ", hctx->fd, strerror(errno)); } - + cgi_handler_ctx_free(hctx); - + con->plugin_ctx[p->id] = NULL; return HANDLER_FINISHED; } @@ -1274,6 +1431,7 @@ SUBREQUEST_FUNC(mod_cgi_handle_subrequest) { } +int mod_cgi_plugin_init(plugin *p); int mod_cgi_plugin_init(plugin *p) { p->version = LIGHTTPD_VERSION_ID; p->name = buffer_init_string("cgi"); @@ -1288,8 +1446,8 @@ int mod_cgi_plugin_init(plugin *p) { p->init = mod_cgi_init; p->cleanup = mod_cgi_free; p->set_defaults = mod_fastcgi_set_defaults; - + p->data = NULL; - + return 0; } diff --git a/src/mod_cml.c b/src/mod_cml.c index 0be9747..f83eb80 100644 --- a/src/mod_cml.c +++ b/src/mod_cml.c @@ -1,12 +1,3 @@ -#include <sys/stat.h> -#include <time.h> - -#include <stdlib.h> -#include <string.h> -#include <errno.h> -#include <unistd.h> -#include <stdio.h> - #include "buffer.h" #include "server.h" #include "log.h" @@ -17,55 +8,62 @@ #include "mod_cml.h" +#include <sys/stat.h> +#include <time.h> + +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <unistd.h> +#include <stdio.h> + /* init the plugin data */ INIT_FUNC(mod_cml_init) { plugin_data *p; - + p = calloc(1, sizeof(*p)); - + p->basedir = buffer_init(); p->baseurl = buffer_init(); - p->session_id = buffer_init(); p->trigger_handler = buffer_init(); - + return p; } /* detroy the plugin data */ FREE_FUNC(mod_cml_free) { plugin_data *p = p_d; - + UNUSED(srv); if (!p) return HANDLER_GO_ON; - + if (p->config_storage) { size_t i; for (i = 0; i < srv->config_context->used; i++) { plugin_config *s = p->config_storage[i]; - + buffer_free(s->ext); - + buffer_free(s->mc_namespace); buffer_free(s->power_magnet); array_free(s->mc_hosts); - + #if defined(HAVE_MEMCACHE_H) if (s->mc) mc_free(s->mc); #endif - + free(s); } free(p->config_storage); } - + buffer_free(p->trigger_handler); - buffer_free(p->session_id); buffer_free(p->basedir); buffer_free(p->baseurl); - + free(p); - + return HANDLER_GO_ON; } @@ -74,22 +72,22 @@ FREE_FUNC(mod_cml_free) { SETDEFAULTS_FUNC(mod_cml_set_defaults) { plugin_data *p = p_d; size_t i = 0; - - config_values_t cv[] = { + + config_values_t cv[] = { { "cml.extension", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 0 */ { "cml.memcache-hosts", NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION }, /* 1 */ { "cml.memcache-namespace", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 2 */ { "cml.power-magnet", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 3 */ { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET } }; - + if (!p) return HANDLER_ERROR; - + p->config_storage = malloc(srv->config_context->used * sizeof(specific_config *)); - + for (i = 0; i < srv->config_context->used; i++) { plugin_config *s; - + s = malloc(sizeof(plugin_config)); s->ext = buffer_init(); s->mc_hosts = array_init(); @@ -98,42 +96,42 @@ SETDEFAULTS_FUNC(mod_cml_set_defaults) { #if defined(HAVE_MEMCACHE_H) s->mc = NULL; #endif - + cv[0].destination = s->ext; cv[1].destination = s->mc_hosts; cv[2].destination = s->mc_namespace; cv[3].destination = s->power_magnet; - + p->config_storage[i] = s; - + if (0 != config_insert_values_global(srv, ((data_config *)srv->config_context->data[i])->value, cv)) { return HANDLER_ERROR; } - + if (s->mc_hosts->used) { #if defined(HAVE_MEMCACHE_H) size_t k; s->mc = mc_new(); - + for (k = 0; k < s->mc_hosts->used; k++) { data_string *ds = (data_string *)s->mc_hosts->data[k]; - + if (0 != mc_server_add4(s->mc, ds->value->ptr)) { - log_error_write(srv, __FILE__, __LINE__, "sb", - "connection to host failed:", + log_error_write(srv, __FILE__, __LINE__, "sb", + "connection to host failed:", ds->value); - + return HANDLER_ERROR; } } #else - log_error_write(srv, __FILE__, __LINE__, "s", + log_error_write(srv, __FILE__, __LINE__, "s", "memcache support is not compiled in but cml.memcache-hosts is set, aborting"); return HANDLER_ERROR; #endif } } - + return HANDLER_GO_ON; } @@ -142,26 +140,26 @@ SETDEFAULTS_FUNC(mod_cml_set_defaults) { static int mod_cml_patch_connection(server *srv, connection *con, plugin_data *p) { size_t i, j; plugin_config *s = p->config_storage[0]; - + PATCH(ext); #if defined(HAVE_MEMCACHE_H) PATCH(mc); #endif PATCH(mc_namespace); PATCH(power_magnet); - + /* skip the first, the global context */ for (i = 1; i < srv->config_context->used; i++) { data_config *dc = (data_config *)srv->config_context->data[i]; s = p->config_storage[i]; - + /* condition didn't match */ if (!config_check_cond(srv, con, dc)) continue; - + /* merge config */ for (j = 0; j < dc->value->used; j++) { data_unset *du = dc->value->data[j]; - + if (buffer_is_equal_string(du->key, CONST_STR_LEN("cml.extension"))) { PATCH(ext); } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("cml.memcache-hosts"))) { @@ -175,191 +173,68 @@ static int mod_cml_patch_connection(server *srv, connection *con, plugin_data *p } } } - + return 0; } #undef PATCH - -int cache_get_cookie_session_id(server *srv, connection *con, plugin_data *p) { - data_unset *d; - - UNUSED(srv); - - if (NULL != (d = array_get_element(con->request.headers, "Cookie"))) { - data_string *ds = (data_string *)d; - size_t key = 0, value = 0; - size_t is_key = 1, is_sid = 0; - size_t i; - - /* found COOKIE */ - if (!DATA_IS_STRING(d)) return -1; - if (ds->value->used == 0) return -1; - - if (ds->value->ptr[0] == '\0' || - ds->value->ptr[0] == '=' || - ds->value->ptr[0] == ';') return -1; - - buffer_reset(p->session_id); - for (i = 0; i < ds->value->used; i++) { - switch(ds->value->ptr[i]) { - case '=': - if (is_key) { - if (0 == strncmp(ds->value->ptr + key, "PHPSESSID", i - key)) { - /* found PHP-session-id-key */ - is_sid = 1; - } - value = i + 1; - - is_key = 0; - } - - break; - case ';': - if (is_sid) { - buffer_copy_string_len(p->session_id, ds->value->ptr + value, i - value); - } - - is_sid = 0; - key = i + 1; - value = 0; - is_key = 1; - break; - case ' ': - if (is_key == 1 && key == i) key = i + 1; - if (is_key == 0 && value == i) value = i + 1; - break; - case '\0': - if (is_sid) { - buffer_copy_string_len(p->session_id, ds->value->ptr + value, i - value); - } - /* fin */ - break; - } - } - } - - return !buffer_is_empty(p->session_id); -} - -int cache_get_url_session_id(server *srv, connection *con, plugin_data *p) { - size_t key = 0, value = 0; - size_t is_key = 1, is_sid = 0; - size_t i; - - UNUSED(srv); - buffer_reset(p->session_id); - for (i = 0; i < con->uri.query->used; i++) { - switch(con->uri.query->ptr[i]) { - case '=': - if (is_key) { - if (0 == strncmp(con->uri.query->ptr + key, "PHPSESSID", i - key)) { - /* found PHP-session-id-key */ - is_sid = 1; - } - value = i + 1; - - is_key = 0; - } - - break; - case '&': - if (is_sid) { - buffer_copy_string_len(p->session_id, con->uri.query->ptr + value, i - value); - } - - is_sid = 0; - key = i + 1; - value = 0; - is_key = 1; - break; - case ' ': - if (is_key == 1 && key == i) key = i + 1; - if (is_key == 0 && value == i) value = i + 1; - break; - case '\0': - if (is_sid) { - buffer_copy_string_len(p->session_id, con->uri.query->ptr + value, i - value); - } - /* fin */ - break; - } - } - - return !buffer_is_empty(p->session_id); -} - -int cache_get_session_id(server *srv, connection *con, plugin_data *p) { - - return cache_get_cookie_session_id(srv, con, p) || - cache_get_url_session_id(srv, con, p); - -} - -int cache_call_lua(server *srv, connection *con, plugin_data *p, buffer *cml_file) { +static int cache_call_lua(server *srv, connection *con, plugin_data *p, buffer *cml_file) { buffer *b; char *c; - int ret; /* cleanup basedir */ b = p->baseurl; buffer_copy_string_buffer(b, con->uri.path); for (c = b->ptr + b->used - 1; c > b->ptr && *c != '/'; c--); - + if (*c == '/') { b->used = c - b->ptr + 2; *(c+1) = '\0'; } - + b = p->basedir; buffer_copy_string_buffer(b, con->physical.path); for (c = b->ptr + b->used - 1; c > b->ptr && *c != '/'; c--); - + if (*c == '/') { b->used = c - b->ptr + 2; *(c+1) = '\0'; } - + /* prepare variables - * - session-id * - cookie-based * - get-param-based */ - - cache_get_session_id(srv, con, p); - return cache_parse_lua(srv, con, p, cml_file); - } URIHANDLER_FUNC(mod_cml_power_magnet) { plugin_data *p = p_d; - + mod_cml_patch_connection(srv, con, p); - + buffer_reset(p->basedir); buffer_reset(p->baseurl); - buffer_reset(p->session_id); buffer_reset(p->trigger_handler); if (buffer_is_empty(p->conf.power_magnet)) return HANDLER_GO_ON; - - /* + + /* * power-magnet: * cml.power-magnet = server.docroot + "/rewrite.cml" * * is called on EACH request, take the original REQUEST_URI and modifies the - * request header as neccesary. + * request header as neccesary. * * First use: * if file_exists("/maintainance.html") { * output_include = ( "/maintainance.html" ) - * return CACHE_HIT + * return CACHE_HIT * } * * as we only want to rewrite HTML like requests we should cover it in a conditional - * + * * */ switch(cache_call_lua(srv, con, p, p->conf.power_magnet)) { @@ -388,21 +263,20 @@ URIHANDLER_FUNC(mod_cml_power_magnet) { URIHANDLER_FUNC(mod_cml_is_handled) { plugin_data *p = p_d; - + if (buffer_is_empty(con->physical.path)) return HANDLER_ERROR; - + mod_cml_patch_connection(srv, con, p); - + buffer_reset(p->basedir); buffer_reset(p->baseurl); - buffer_reset(p->session_id); buffer_reset(p->trigger_handler); if (buffer_is_empty(p->conf.ext)) return HANDLER_GO_ON; - + if (!buffer_is_equal_right_len(con->physical.path, p->conf.ext, p->conf.ext->used - 1)) { return HANDLER_GO_ON; - } + } switch(cache_call_lua(srv, con, p, con->physical.path)) { case -1: @@ -431,18 +305,19 @@ URIHANDLER_FUNC(mod_cml_is_handled) { } } +int mod_cml_plugin_init(plugin *p); int mod_cml_plugin_init(plugin *p) { p->version = LIGHTTPD_VERSION_ID; p->name = buffer_init_string("cache"); - + p->init = mod_cml_init; p->cleanup = mod_cml_free; p->set_defaults = mod_cml_set_defaults; - + p->handle_subrequest_start = mod_cml_is_handled; p->handle_physical = mod_cml_power_magnet; - + p->data = NULL; - + return 0; } diff --git a/src/mod_cml.h b/src/mod_cml.h index a2e9df4..1afd708 100644 --- a/src/mod_cml.h +++ b/src/mod_cml.h @@ -16,10 +16,10 @@ typedef struct { buffer *ext; - + array *mc_hosts; buffer *mc_namespace; -#if defined(HAVE_MEMCACHE_H) +#if defined(HAVE_MEMCACHE_H) struct memcache *mc; #endif buffer *power_magnet; @@ -27,17 +27,15 @@ typedef struct { typedef struct { PLUGIN_DATA; - + buffer *basedir; buffer *baseurl; - + buffer *trigger_handler; - - buffer *session_id; - + plugin_config **config_storage; - - plugin_config conf; + + plugin_config conf; } plugin_data; int cache_parse_lua(server *srv, connection *con, plugin_data *p, buffer *fn); diff --git a/src/mod_cml_funcs.c b/src/mod_cml_funcs.c index 46a1f16..9d859c7 100644 --- a/src/mod_cml_funcs.c +++ b/src/mod_cml_funcs.c @@ -1,3 +1,12 @@ +#include "buffer.h" +#include "server.h" +#include "log.h" +#include "plugin.h" +#include "response.h" + +#include "mod_cml.h" +#include "mod_cml_funcs.h" + #include <sys/stat.h> #include <time.h> @@ -8,20 +17,7 @@ #include <dirent.h> #include <stdio.h> -#include "buffer.h" -#include "server.h" -#include "log.h" -#include "plugin.h" -#include "response.h" - -#include "mod_cml.h" -#include "mod_cml_funcs.h" - -#ifdef USE_OPENSSL -# include <openssl/md5.h> -#else -# include "md5.h" -#endif +#include "md5.h" #define HASHLEN 16 typedef unsigned char HASH[HASHLEN]; @@ -30,41 +26,41 @@ typedef char HASHHEX[HASHHEXLEN+1]; #ifdef USE_OPENSSL #define IN const #else -#define IN +#define IN #endif #define OUT #ifdef HAVE_LUA_H int f_crypto_md5(lua_State *L) { - MD5_CTX Md5Ctx; + li_MD5_CTX Md5Ctx; HASH HA1; buffer b; char hex[33]; int n = lua_gettop(L); - + b.ptr = hex; b.used = 0; b.size = sizeof(hex); - + if (n != 1) { lua_pushstring(L, "md5: expected one argument"); lua_error(L); } - + if (!lua_isstring(L, 1)) { lua_pushstring(L, "md5: argument has to be a string"); lua_error(L); } - - MD5_Init(&Md5Ctx); - MD5_Update(&Md5Ctx, (unsigned char *)lua_tostring(L, 1), lua_strlen(L, 1)); - MD5_Final(HA1, &Md5Ctx); - + + li_MD5_Init(&Md5Ctx); + li_MD5_Update(&Md5Ctx, (unsigned char *)lua_tostring(L, 1), lua_strlen(L, 1)); + li_MD5_Final(HA1, &Md5Ctx); + buffer_copy_string_hex(&b, (char *)HA1, 16); - + lua_pushstring(L, b.ptr); - + return 1; } @@ -72,37 +68,37 @@ int f_crypto_md5(lua_State *L) { int f_file_mtime(lua_State *L) { struct stat st; int n = lua_gettop(L); - + if (n != 1) { lua_pushstring(L, "file_mtime: expected one argument"); lua_error(L); } - + if (!lua_isstring(L, 1)) { lua_pushstring(L, "file_mtime: argument has to be a string"); lua_error(L); } - + if (-1 == stat(lua_tostring(L, 1), &st)) { lua_pushnil(L); return 1; } - + lua_pushnumber(L, st.st_mtime); - + return 1; } -int f_dir_files_iter(lua_State *L) { +static int f_dir_files_iter(lua_State *L) { DIR *d; struct dirent *de; - + d = lua_touserdata(L, lua_upvalueindex(1)); - + if (NULL == (de = readdir(d))) { /* EOF */ closedir(d); - + return 0; } else { lua_pushstring(L, de->d_name); @@ -113,75 +109,75 @@ int f_dir_files_iter(lua_State *L) { int f_dir_files(lua_State *L) { DIR *d; int n = lua_gettop(L); - + if (n != 1) { lua_pushstring(L, "dir_files: expected one argument"); lua_error(L); } - + if (!lua_isstring(L, 1)) { lua_pushstring(L, "dir_files: argument has to be a string"); lua_error(L); } - - /* check if there is a valid DIR handle on the stack */ + + /* check if there is a valid DIR handle on the stack */ if (NULL == (d = opendir(lua_tostring(L, 1)))) { lua_pushnil(L); return 1; } - + /* push d into registry */ lua_pushlightuserdata(L, d); lua_pushcclosure(L, f_dir_files_iter, 1); - + return 1; } int f_file_isreg(lua_State *L) { struct stat st; int n = lua_gettop(L); - + if (n != 1) { lua_pushstring(L, "file_isreg: expected one argument"); lua_error(L); } - + if (!lua_isstring(L, 1)) { lua_pushstring(L, "file_isreg: argument has to be a string"); lua_error(L); } - + if (-1 == stat(lua_tostring(L, 1), &st)) { lua_pushnil(L); return 1; } - + lua_pushnumber(L, S_ISREG(st.st_mode)); - + return 1; } int f_file_isdir(lua_State *L) { struct stat st; int n = lua_gettop(L); - + if (n != 1) { lua_pushstring(L, "file_isreg: expected one argument"); lua_error(L); } - + if (!lua_isstring(L, 1)) { lua_pushstring(L, "file_isreg: argument has to be a string"); lua_error(L); } - + if (-1 == stat(lua_tostring(L, 1), &st)) { lua_pushnil(L); return 1; } - + lua_pushnumber(L, S_ISDIR(st.st_mode)); - + return 1; } @@ -192,33 +188,33 @@ int f_memcache_exists(lua_State *L) { char *r; int n = lua_gettop(L); struct memcache *mc; - + if (!lua_islightuserdata(L, lua_upvalueindex(1))) { lua_pushstring(L, "where is my userdata ?"); lua_error(L); } - + mc = lua_touserdata(L, lua_upvalueindex(1)); - + if (n != 1) { lua_pushstring(L, "expected one argument"); lua_error(L); } - + if (!lua_isstring(L, 1)) { lua_pushstring(L, "argument has to be a string"); lua_error(L); } - - if (NULL == (r = mc_aget(mc, - lua_tostring(L, 1), lua_strlen(L, 1)))) { - + + if (NULL == (r = mc_aget(mc, + (char*) lua_tostring(L, 1), lua_strlen(L, 1)))) { + lua_pushboolean(L, 0); return 1; } - + free(r); - + lua_pushboolean(L, 1); return 1; } @@ -226,74 +222,74 @@ int f_memcache_exists(lua_State *L) { int f_memcache_get_string(lua_State *L) { char *r; int n = lua_gettop(L); - + struct memcache *mc; - + if (!lua_islightuserdata(L, lua_upvalueindex(1))) { lua_pushstring(L, "where is my userdata ?"); lua_error(L); } - + mc = lua_touserdata(L, lua_upvalueindex(1)); - - + + if (n != 1) { lua_pushstring(L, "expected one argument"); lua_error(L); } - + if (!lua_isstring(L, 1)) { lua_pushstring(L, "argument has to be a string"); lua_error(L); } - - if (NULL == (r = mc_aget(mc, - lua_tostring(L, 1), lua_strlen(L, 1)))) { + + if (NULL == (r = mc_aget(mc, + (char*) lua_tostring(L, 1), lua_strlen(L, 1)))) { lua_pushnil(L); return 1; } - + lua_pushstring(L, r); - + free(r); - + return 1; } int f_memcache_get_long(lua_State *L) { char *r; int n = lua_gettop(L); - + struct memcache *mc; - + if (!lua_islightuserdata(L, lua_upvalueindex(1))) { lua_pushstring(L, "where is my userdata ?"); lua_error(L); } - + mc = lua_touserdata(L, lua_upvalueindex(1)); - - + + if (n != 1) { lua_pushstring(L, "expected one argument"); lua_error(L); } - + if (!lua_isstring(L, 1)) { lua_pushstring(L, "argument has to be a string"); lua_error(L); } - - if (NULL == (r = mc_aget(mc, - lua_tostring(L, 1), lua_strlen(L, 1)))) { + + if (NULL == (r = mc_aget(mc, + (char*) lua_tostring(L, 1), lua_strlen(L, 1)))) { lua_pushnil(L); return 1; } - + lua_pushnumber(L, strtol(r, NULL, 10)); - + free(r); - + return 1; } #endif diff --git a/src/mod_cml_funcs.h b/src/mod_cml_funcs.h index 42e067c..f0695d7 100644 --- a/src/mod_cml_funcs.h +++ b/src/mod_cml_funcs.h @@ -2,7 +2,7 @@ #define _MOD_CML_FUNCS_H_ #ifdef HAVE_CONFIG_H -#include "config.h" +# include "config.h" #endif #ifdef HAVE_LUA_H diff --git a/src/mod_cml_lua.c b/src/mod_cml_lua.c index 4e780e1..9f4e27a 100644 --- a/src/mod_cml_lua.c +++ b/src/mod_cml_lua.c @@ -1,8 +1,3 @@ -#include <assert.h> -#include <stdio.h> -#include <errno.h> -#include <time.h> - #include "mod_cml.h" #include "mod_cml_funcs.h" #include "log.h" @@ -10,11 +5,11 @@ #include "stat_cache.h" -#ifdef USE_OPENSSL -# include <openssl/md5.h> -#else -# include "md5.h" -#endif +#include <assert.h> +#include <stdio.h> +#include <errno.h> +#include <time.h> +#include <string.h> #define HASHLEN 16 typedef unsigned char HASH[HASHLEN]; @@ -23,7 +18,7 @@ typedef char HASHHEX[HASHHEXLEN+1]; #ifdef USE_OPENSSL #define IN const #else -#define IN +#define IN #endif #define OUT @@ -31,6 +26,7 @@ typedef char HASHHEX[HASHHEXLEN+1]; #include <lua.h> #include <lualib.h> +#include <lauxlib.h> typedef struct { stream st; @@ -39,11 +35,11 @@ typedef struct { static const char * load_file(lua_State *L, void *data, size_t *size) { readme *rm = data; - + UNUSED(L); - + if (rm->done) return 0; - + *size = rm->st.size; rm->done = 1; return rm->st.start; @@ -51,47 +47,47 @@ static const char * load_file(lua_State *L, void *data, size_t *size) { static int lua_to_c_get_string(lua_State *L, const char *varname, buffer *b) { int curelem; - + lua_pushstring(L, varname); - + curelem = lua_gettop(L); lua_gettable(L, LUA_GLOBALSINDEX); - + /* it should be a table */ if (!lua_isstring(L, curelem)) { lua_settop(L, curelem - 1); - + return -1; } - + buffer_copy_string(b, lua_tostring(L, curelem)); - + lua_pop(L, 1); - + assert(curelem - 1 == lua_gettop(L)); - + return 0; } static int lua_to_c_is_table(lua_State *L, const char *varname) { int curelem; - + lua_pushstring(L, varname); - + curelem = lua_gettop(L); lua_gettable(L, LUA_GLOBALSINDEX); - + /* it should be a table */ if (!lua_istable(L, curelem)) { lua_settop(L, curelem - 1); - + return 0; } - + lua_settop(L, curelem - 1); - + assert(curelem - 1 == lua_gettop(L)); - + return 1; } @@ -99,88 +95,148 @@ static int c_to_lua_push(lua_State *L, int tbl, const char *key, size_t key_len, lua_pushlstring(L, key, key_len); lua_pushlstring(L, val, val_len); lua_settable(L, tbl); - + return 0; } -int split_query_string(lua_State *L, int tbl, buffer *qrystr) { +static int cache_export_get_params(lua_State *L, int tbl, buffer *qrystr) { size_t is_key = 1; size_t i; char *key = NULL, *val = NULL; - + key = qrystr->ptr; - + /* we need the \0 */ for (i = 0; i < qrystr->used; i++) { switch(qrystr->ptr[i]) { case '=': if (is_key) { val = qrystr->ptr + i + 1; - + qrystr->ptr[i] = '\0'; - + is_key = 0; } - + break; case '&': case '\0': /* fin symbol */ if (!is_key) { /* we need at least a = since the last & */ - - c_to_lua_push(L, tbl, + + /* terminate the value */ + qrystr->ptr[i] = '\0'; + + c_to_lua_push(L, tbl, key, strlen(key), val, strlen(val)); } - + key = qrystr->ptr + i + 1; val = NULL; is_key = 1; break; } } - + return 0; } +#if 0 +int cache_export_cookie_params(server *srv, connection *con, plugin_data *p) { + data_unset *d; + + UNUSED(srv); + + if (NULL != (d = array_get_element(con->request.headers, "Cookie"))) { + data_string *ds = (data_string *)d; + size_t key = 0, value = 0; + size_t is_key = 1, is_sid = 0; + size_t i; + + /* found COOKIE */ + if (!DATA_IS_STRING(d)) return -1; + if (ds->value->used == 0) return -1; + + if (ds->value->ptr[0] == '\0' || + ds->value->ptr[0] == '=' || + ds->value->ptr[0] == ';') return -1; + + buffer_reset(p->session_id); + for (i = 0; i < ds->value->used; i++) { + switch(ds->value->ptr[i]) { + case '=': + if (is_key) { + if (0 == strncmp(ds->value->ptr + key, "PHPSESSID", i - key)) { + /* found PHP-session-id-key */ + is_sid = 1; + } + value = i + 1; + + is_key = 0; + } + + break; + case ';': + if (is_sid) { + buffer_copy_string_len(p->session_id, ds->value->ptr + value, i - value); + } + + is_sid = 0; + key = i + 1; + value = 0; + is_key = 1; + break; + case ' ': + if (is_key == 1 && key == i) key = i + 1; + if (is_key == 0 && value == i) value = i + 1; + break; + case '\0': + if (is_sid) { + buffer_copy_string_len(p->session_id, ds->value->ptr + value, i - value); + } + /* fin */ + break; + } + } + } + + return 0; +} +#endif int cache_parse_lua(server *srv, connection *con, plugin_data *p, buffer *fn) { - lua_State *L; + lua_State *L; readme rm; int ret = -1; buffer *b = buffer_init(); int header_tbl = 0; - + rm.done = 0; stream_open(&rm.st, fn); - + /* push the lua file to the interpreter and see what happends */ - L = lua_open(); - - luaopen_base(L); - luaopen_table(L); - luaopen_string(L); - luaopen_math(L); - luaopen_io(L); - + L = luaL_newstate(); + luaL_openlibs(L); + /* register functions */ lua_register(L, "md5", f_crypto_md5); lua_register(L, "file_mtime", f_file_mtime); lua_register(L, "file_isreg", f_file_isreg); lua_register(L, "file_isdir", f_file_isreg); lua_register(L, "dir_files", f_dir_files); - + #ifdef HAVE_MEMCACHE_H lua_pushliteral(L, "memcache_get_long"); lua_pushlightuserdata(L, p->conf.mc); lua_pushcclosure(L, f_memcache_get_long, 1); lua_settable(L, LUA_GLOBALSINDEX); - + lua_pushliteral(L, "memcache_get_string"); lua_pushlightuserdata(L, p->conf.mc); lua_pushcclosure(L, f_memcache_get_string, 1); lua_settable(L, LUA_GLOBALSINDEX); - + lua_pushliteral(L, "memcache_exists"); lua_pushlightuserdata(L, p->conf.mc); lua_pushcclosure(L, f_memcache_exists, 1); @@ -190,11 +246,11 @@ int cache_parse_lua(server *srv, connection *con, plugin_data *p, buffer *fn) { lua_pushliteral(L, "request"); lua_newtable(L); lua_settable(L, LUA_GLOBALSINDEX); - + lua_pushliteral(L, "request"); header_tbl = lua_gettop(L); lua_gettable(L, LUA_GLOBALSINDEX); - + c_to_lua_push(L, header_tbl, CONST_STR_LEN("REQUEST_URI"), CONST_BUF_LEN(con->request.orig_uri)); c_to_lua_push(L, header_tbl, CONST_STR_LEN("SCRIPT_NAME"), CONST_BUF_LEN(con->uri.path)); c_to_lua_push(L, header_tbl, CONST_STR_LEN("SCRIPT_FILENAME"), CONST_BUF_LEN(con->physical.path)); @@ -202,84 +258,84 @@ int cache_parse_lua(server *srv, connection *con, plugin_data *p, buffer *fn) { if (!buffer_is_empty(con->request.pathinfo)) { c_to_lua_push(L, header_tbl, CONST_STR_LEN("PATH_INFO"), CONST_BUF_LEN(con->request.pathinfo)); } - + c_to_lua_push(L, header_tbl, CONST_STR_LEN("CWD"), CONST_BUF_LEN(p->basedir)); c_to_lua_push(L, header_tbl, CONST_STR_LEN("BASEURL"), CONST_BUF_LEN(p->baseurl)); - + /* register GET parameter */ lua_pushliteral(L, "get"); lua_newtable(L); lua_settable(L, LUA_GLOBALSINDEX); - + lua_pushliteral(L, "get"); header_tbl = lua_gettop(L); lua_gettable(L, LUA_GLOBALSINDEX); - - + buffer_copy_string_buffer(b, con->uri.query); - split_query_string(L, header_tbl, b); + cache_export_get_params(L, header_tbl, b); buffer_reset(b); - + + /* 2 default constants */ lua_pushliteral(L, "CACHE_HIT"); lua_pushnumber(L, 0); lua_settable(L, LUA_GLOBALSINDEX); - + lua_pushliteral(L, "CACHE_MISS"); lua_pushnumber(L, 1); lua_settable(L, LUA_GLOBALSINDEX); - + /* load lua program */ if (lua_load(L, load_file, &rm, fn->ptr) || lua_pcall(L,0,1,0)) { log_error_write(srv, __FILE__, __LINE__, "s", lua_tostring(L,-1)); - + goto error; } - + /* get return value */ ret = (int)lua_tonumber(L, -1); lua_pop(L, 1); - - /* fetch the data from lua */ + + /* fetch the data from lua */ lua_to_c_get_string(L, "trigger_handler", p->trigger_handler); - + if (0 == lua_to_c_get_string(L, "output_contenttype", b)) { response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_BUF_LEN(b)); } - + if (ret == 0) { /* up to now it is a cache-hit, check if all files exist */ - + int curelem; time_t mtime = 0; - + if (!lua_to_c_is_table(L, "output_include")) { log_error_write(srv, __FILE__, __LINE__, "s", "output_include is missing or not a table"); ret = -1; - + goto error; } - + lua_pushstring(L, "output_include"); - + curelem = lua_gettop(L); lua_gettable(L, LUA_GLOBALSINDEX); /* HOW-TO build a etag ? - * as we don't just have one file we have to take the stat() + * as we don't just have one file we have to take the stat() * from all base files, merge them and build the etag from * it later. - * + * * The mtime of the content is the mtime of the freshest base file - * + * * */ - + lua_pushnil(L); /* first key */ while (lua_next(L, curelem) != 0) { stat_cache_entry *sce = NULL; /* key' is at index -2 and value' at index -1 */ - + if (lua_isstring(L, -1)) { const char *s = lua_tostring(L, -1); @@ -299,18 +355,18 @@ int cache_parse_lua(server *srv, connection *con, plugin_data *p, buffer *fn) { /* a file is missing, call the handler to generate it */ if (!buffer_is_empty(p->trigger_handler)) { ret = 1; /* cache-miss */ - + log_error_write(srv, __FILE__, __LINE__, "s", "a file is missing, calling handler"); - + break; } else { /* handler not set -> 500 */ ret = -1; - + log_error_write(srv, __FILE__, __LINE__, "s", "a file missing and no handler set"); - + break; } break; @@ -328,12 +384,12 @@ int cache_parse_lua(server *srv, connection *con, plugin_data *p, buffer *fn) { "not a string"); break; } - + lua_pop(L, 1); /* removes value'; keeps key' for next iteration */ } - + lua_settop(L, curelem - 1); - + if (ret == 0) { data_string *ds; char timebuf[sizeof("Sat, 23 Jul 2005 21:20:01 GMT")]; @@ -345,9 +401,9 @@ int cache_parse_lua(server *srv, connection *con, plugin_data *p, buffer *fn) { /* no Last-Modified specified */ if ((mtime) && (NULL == ds)) { - + strftime(timebuf, sizeof(timebuf), "%a, %d %b %Y %H:%M:%S GMT", gmtime(&mtime)); - + response_header_overwrite(srv, con, CONST_STR_LEN("Last-Modified"), timebuf, sizeof(timebuf) - 1); @@ -363,9 +419,9 @@ int cache_parse_lua(server *srv, connection *con, plugin_data *p, buffer *fn) { tbuf.used = 0; tbuf.ptr = NULL; } - + if (HANDLER_FINISHED == http_response_handle_cachable(srv, con, &tbuf)) { - /* ok, the client already has our content, + /* ok, the client already has our content, * no need to send it again */ chunkqueue_reset(con->write_queue); @@ -375,28 +431,32 @@ int cache_parse_lua(server *srv, connection *con, plugin_data *p, buffer *fn) { chunkqueue_reset(con->write_queue); } } - + if (ret == 1 && !buffer_is_empty(p->trigger_handler)) { /* cache-miss */ buffer_copy_string_buffer(con->uri.path, p->baseurl); buffer_append_string_buffer(con->uri.path, p->trigger_handler); - + buffer_copy_string_buffer(con->physical.path, p->basedir); buffer_append_string_buffer(con->physical.path, p->trigger_handler); - + chunkqueue_reset(con->write_queue); } - + error: lua_close(L); - + stream_close(&rm.st); buffer_free(b); - + return ret /* cache-error */; } #else int cache_parse_lua(server *srv, connection *con, plugin_data *p, buffer *fn) { + UNUSED(srv); + UNUSED(con); + UNUSED(p); + UNUSED(fn); /* error */ return -1; } diff --git a/src/mod_compress.c b/src/mod_compress.c index fc78220..e4f53da 100644 --- a/src/mod_compress.c +++ b/src/mod_compress.c @@ -1,14 +1,3 @@ -#include <sys/types.h> -#include <sys/stat.h> - -#include <fcntl.h> -#include <unistd.h> -#include <ctype.h> -#include <stdlib.h> -#include <string.h> -#include <errno.h> -#include <time.h> - #include "base.h" #include "log.h" #include "buffer.h" @@ -20,6 +9,17 @@ #include "crc32.h" #include "etag.h" +#include <sys/types.h> +#include <sys/stat.h> + +#include <fcntl.h> +#include <unistd.h> +#include <ctype.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <time.h> + #if defined HAVE_ZLIB_H && defined HAVE_LIBZ # define USE_ZLIB # include <zlib.h> @@ -40,112 +40,202 @@ #define HTTP_ACCEPT_ENCODING_DEFLATE BV(2) #define HTTP_ACCEPT_ENCODING_COMPRESS BV(3) #define HTTP_ACCEPT_ENCODING_BZIP2 BV(4) +#define HTTP_ACCEPT_ENCODING_X_GZIP BV(5) +#define HTTP_ACCEPT_ENCODING_X_BZIP2 BV(6) #ifdef __WIN32 -#define mkdir(x,y) mkdir(x) +# define mkdir(x,y) mkdir(x) #endif typedef struct { buffer *compress_cache_dir; array *compress; off_t compress_max_filesize; /** max filesize in kb */ + int allowed_encodings; } plugin_config; typedef struct { PLUGIN_DATA; buffer *ofn; buffer *b; - + plugin_config **config_storage; - plugin_config conf; + plugin_config conf; } plugin_data; INIT_FUNC(mod_compress_init) { plugin_data *p; - + p = calloc(1, sizeof(*p)); - + p->ofn = buffer_init(); p->b = buffer_init(); - + return p; } FREE_FUNC(mod_compress_free) { plugin_data *p = p_d; - + UNUSED(srv); if (!p) return HANDLER_GO_ON; - + buffer_free(p->ofn); buffer_free(p->b); - + if (p->config_storage) { size_t i; for (i = 0; i < srv->config_context->used; i++) { plugin_config *s = p->config_storage[i]; if (!s) continue; - + array_free(s->compress); buffer_free(s->compress_cache_dir); - + free(s); } free(p->config_storage); } - - + + free(p); - + return HANDLER_GO_ON; } +/* 0 on success, -1 for error */ +static int mkdir_recursive(char *dir) { + char *p = dir; + + if (!dir || !dir[0]) + return 0; + + while ((p = strchr(p + 1, '/')) != NULL) { + + *p = '\0'; + if ((mkdir(dir, 0700) != 0) && (errno != EEXIST)) { + *p = '/'; + return -1; + } + + *p++ = '/'; + if (!*p) return 0; /* Ignore trailing slash */ + } + + return (mkdir(dir, 0700) != 0) && (errno != EEXIST) ? -1 : 0; +} + +/* 0 on success, -1 for error */ +static int mkdir_for_file(char *filename) { + char *p = filename; + + if (!filename || !filename[0]) + return -1; + + while ((p = strchr(p + 1, '/')) != NULL) { + + *p = '\0'; + if ((mkdir(filename, 0700) != 0) && (errno != EEXIST)) { + *p = '/'; + return -1; + } + + *p++ = '/'; + if (!*p) return -1; /* Unexpected trailing slash in filename */ + } + + return 0; +} + SETDEFAULTS_FUNC(mod_compress_setdefaults) { plugin_data *p = p_d; size_t i = 0; - - config_values_t cv[] = { + + config_values_t cv[] = { { "compress.cache-dir", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, { "compress.filetype", NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION }, { "compress.max-filesize", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, + { "compress.allowed-encodings", NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION }, { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET } }; - + p->config_storage = calloc(1, srv->config_context->used * sizeof(specific_config *)); - + for (i = 0; i < srv->config_context->used; i++) { plugin_config *s; - + array *encodings_arr = array_init(); + s = calloc(1, sizeof(plugin_config)); s->compress_cache_dir = buffer_init(); s->compress = array_init(); s->compress_max_filesize = 0; - + s->allowed_encodings = 0; + cv[0].destination = s->compress_cache_dir; cv[1].destination = s->compress; cv[2].destination = &(s->compress_max_filesize); - + cv[3].destination = encodings_arr; /* temp array for allowed encodings list */ + p->config_storage[i] = s; - + if (0 != config_insert_values_global(srv, ((data_config *)srv->config_context->data[i])->value, cv)) { return HANDLER_ERROR; } - + + if (encodings_arr->used) { + size_t j = 0; + for (j = 0; j < encodings_arr->used; j++) { + data_string *ds = (data_string *)encodings_arr->data[j]; +#ifdef USE_ZLIB + if (NULL != strstr(ds->value->ptr, "gzip")) + s->allowed_encodings |= HTTP_ACCEPT_ENCODING_GZIP | HTTP_ACCEPT_ENCODING_X_GZIP; + if (NULL != strstr(ds->value->ptr, "x-gzip")) + s->allowed_encodings |= HTTP_ACCEPT_ENCODING_X_GZIP; + if (NULL != strstr(ds->value->ptr, "deflate")) + s->allowed_encodings |= HTTP_ACCEPT_ENCODING_DEFLATE; + /* + if (NULL != strstr(ds->value->ptr, "compress")) + s->allowed_encodings |= HTTP_ACCEPT_ENCODING_COMPRESS; + */ +#endif +#ifdef USE_BZ2LIB + if (NULL != strstr(ds->value->ptr, "bzip2")) + s->allowed_encodings |= HTTP_ACCEPT_ENCODING_BZIP2 | HTTP_ACCEPT_ENCODING_X_BZIP2; + if (NULL != strstr(ds->value->ptr, "x-bzip2")) + s->allowed_encodings |= HTTP_ACCEPT_ENCODING_X_BZIP2; +#endif + } + } else { + /* default encodings */ + s->allowed_encodings = 0 +#ifdef USE_ZLIB + | HTTP_ACCEPT_ENCODING_GZIP | HTTP_ACCEPT_ENCODING_X_GZIP | HTTP_ACCEPT_ENCODING_DEFLATE +#endif +#ifdef USE_BZ2LIB + | HTTP_ACCEPT_ENCODING_BZIP2 | HTTP_ACCEPT_ENCODING_X_BZIP2 +#endif + ; + } + + array_free(encodings_arr); + if (!buffer_is_empty(s->compress_cache_dir)) { struct stat st; + mkdir_recursive(s->compress_cache_dir->ptr); + if (0 != stat(s->compress_cache_dir->ptr, &st)) { - log_error_write(srv, __FILE__, __LINE__, "sbs", "can't stat compress.cache-dir", + log_error_write(srv, __FILE__, __LINE__, "sbs", "can't stat compress.cache-dir", s->compress_cache_dir, strerror(errno)); - + return HANDLER_ERROR; } } } - + return HANDLER_GO_ON; - + } #ifdef USE_ZLIB @@ -153,32 +243,32 @@ static int deflate_file_to_buffer_gzip(server *srv, connection *con, plugin_data unsigned char *c; unsigned long crc; z_stream z; - + UNUSED(srv); UNUSED(con); z.zalloc = Z_NULL; z.zfree = Z_NULL; z.opaque = Z_NULL; - - if (Z_OK != deflateInit2(&z, + + if (Z_OK != deflateInit2(&z, Z_DEFAULT_COMPRESSION, - Z_DEFLATED, + Z_DEFLATED, -MAX_WBITS, /* supress zlib-header */ 8, Z_DEFAULT_STRATEGY)) { return -1; } - + z.next_in = (unsigned char *)start; z.avail_in = st_size; z.total_in = 0; - - + + buffer_prepare_copy(p->b, (z.avail_in * 1.1) + 12 + 18); - + /* write gzip header */ - + c = (unsigned char *)p->b->ptr; c[0] = 0x1f; c[1] = 0x8b; @@ -190,24 +280,24 @@ static int deflate_file_to_buffer_gzip(server *srv, connection *con, plugin_data c[7] = (mtime >> 24) & 0xff; c[8] = 0x00; /* extra flags */ c[9] = 0x03; /* UNIX */ - + p->b->used = 10; z.next_out = (unsigned char *)p->b->ptr + p->b->used; z.avail_out = p->b->size - p->b->used - 8; z.total_out = 0; - + if (Z_STREAM_END != deflate(&z, Z_FINISH)) { deflateEnd(&z); return -1; } - + /* trailer */ p->b->used += z.total_out; - + crc = generate_crc32c(start, st_size); - + c = (unsigned char *)p->b->ptr + p->b->used; - + c[0] = (crc >> 0) & 0xff; c[1] = (crc >> 8) & 0xff; c[2] = (crc >> 16) & 0xff; @@ -221,51 +311,51 @@ static int deflate_file_to_buffer_gzip(server *srv, connection *con, plugin_data if (Z_OK != deflateEnd(&z)) { return -1; } - + return 0; } static int deflate_file_to_buffer_deflate(server *srv, connection *con, plugin_data *p, unsigned char *start, off_t st_size) { z_stream z; - + UNUSED(srv); UNUSED(con); z.zalloc = Z_NULL; z.zfree = Z_NULL; z.opaque = Z_NULL; - - if (Z_OK != deflateInit2(&z, + + if (Z_OK != deflateInit2(&z, Z_DEFAULT_COMPRESSION, - Z_DEFLATED, + Z_DEFLATED, -MAX_WBITS, /* supress zlib-header */ 8, Z_DEFAULT_STRATEGY)) { return -1; } - + z.next_in = start; z.avail_in = st_size; z.total_in = 0; - + buffer_prepare_copy(p->b, (z.avail_in * 1.1) + 12); - + z.next_out = (unsigned char *)p->b->ptr; z.avail_out = p->b->size; z.total_out = 0; - + if (Z_STREAM_END != deflate(&z, Z_FINISH)) { deflateEnd(&z); return -1; } - + /* trailer */ p->b->used += z.total_out; - + if (Z_OK != deflateEnd(&z)) { return -1; } - + return 0; } @@ -274,48 +364,48 @@ static int deflate_file_to_buffer_deflate(server *srv, connection *con, plugin_d #ifdef USE_BZ2LIB static int deflate_file_to_buffer_bzip2(server *srv, connection *con, plugin_data *p, unsigned char *start, off_t st_size) { bz_stream bz; - + UNUSED(srv); UNUSED(con); bz.bzalloc = NULL; bz.bzfree = NULL; bz.opaque = NULL; - - if (BZ_OK != BZ2_bzCompressInit(&bz, + + if (BZ_OK != BZ2_bzCompressInit(&bz, 9, /* blocksize = 900k */ 0, /* no output */ 0)) { /* workFactor: default */ return -1; } - + bz.next_in = (char *)start; bz.avail_in = st_size; bz.total_in_lo32 = 0; bz.total_in_hi32 = 0; - + buffer_prepare_copy(p->b, (bz.avail_in * 1.1) + 12); - + bz.next_out = p->b->ptr; bz.avail_out = p->b->size; bz.total_out_lo32 = 0; bz.total_out_hi32 = 0; - + if (BZ_STREAM_END != BZ2_bzCompress(&bz, BZ_FINISH)) { BZ2_bzCompressEnd(&bz); return -1; } - + /* file is too large for now */ if (bz.total_out_hi32) return -1; - + /* trailer */ p->b->used = bz.total_out_lo32; - + if (BZ_OK != BZ2_bzCompressEnd(&bz)) { return -1; } - + return 0; } #endif @@ -326,64 +416,52 @@ static int deflate_file_to_file(server *srv, connection *con, plugin_data *p, bu void *start; const char *filename = fn->ptr; ssize_t r; - + /* overflow */ if ((off_t)(sce->st.st_size * 1.1) < sce->st.st_size) return -1; - - /* don't mmap files > 128Mb - * + + /* don't mmap files > 128Mb + * * we could use a sliding window, but currently there is no need for it */ - + if (sce->st.st_size > 128 * 1024 * 1024) return -1; - + buffer_reset(p->ofn); buffer_copy_string_buffer(p->ofn, p->conf.compress_cache_dir); BUFFER_APPEND_SLASH(p->ofn); - + if (0 == strncmp(con->physical.path->ptr, con->physical.doc_root->ptr, con->physical.doc_root->used-1)) { - size_t offset = p->ofn->used - 1; - char *dir, *nextdir; - buffer_append_string(p->ofn, con->physical.path->ptr + con->physical.doc_root->used - 1); - buffer_copy_string_buffer(p->b, p->ofn); - - /* mkdir -p ... */ - for (dir = p->b->ptr + offset; NULL != (nextdir = strchr(dir, '/')); dir = nextdir + 1) { - *nextdir = '\0'; - - if (-1 == mkdir(p->b->ptr, 0700)) { - if (errno != EEXIST) { - log_error_write(srv, __FILE__, __LINE__, "sbss", "creating cache-directory", p->b, "failed", strerror(errno)); - - return -1; - } - } - - *nextdir = '/'; - } } else { buffer_append_string_buffer(p->ofn, con->uri.path); } - + switch(type) { case HTTP_ACCEPT_ENCODING_GZIP: - buffer_append_string(p->ofn, "-gzip-"); + case HTTP_ACCEPT_ENCODING_X_GZIP: + buffer_append_string_len(p->ofn, CONST_STR_LEN("-gzip-")); break; case HTTP_ACCEPT_ENCODING_DEFLATE: - buffer_append_string(p->ofn, "-deflate-"); + buffer_append_string_len(p->ofn, CONST_STR_LEN("-deflate-")); break; case HTTP_ACCEPT_ENCODING_BZIP2: - buffer_append_string(p->ofn, "-bzip2-"); + case HTTP_ACCEPT_ENCODING_X_BZIP2: + buffer_append_string_len(p->ofn, CONST_STR_LEN("-bzip2-")); break; default: log_error_write(srv, __FILE__, __LINE__, "sd", "unknown compression type", type); return -1; } - + buffer_append_string_buffer(p->ofn, sce->etag); - + + if (-1 == mkdir_for_file(p->ofn->ptr)) { + log_error_write(srv, __FILE__, __LINE__, "sb", "couldn't create directory for file", p->ofn); + return -1; + } + if (-1 == (ofd = open(p->ofn->ptr, O_WRONLY | O_CREAT | O_EXCL | O_BINARY, 0600))) { if (errno == EEXIST) { /* cache-entry exists */ @@ -391,45 +469,75 @@ static int deflate_file_to_file(server *srv, connection *con, plugin_data *p, bu log_error_write(srv, __FILE__, __LINE__, "bs", p->ofn, "compress-cache hit"); #endif buffer_copy_string_buffer(con->physical.path, p->ofn); - + return 0; } - + log_error_write(srv, __FILE__, __LINE__, "sbss", "creating cachefile", p->ofn, "failed", strerror(errno)); - + return -1; } #if 0 log_error_write(srv, __FILE__, __LINE__, "bs", p->ofn, "compress-cache miss"); -#endif +#endif if (-1 == (ifd = open(filename, O_RDONLY | O_BINARY))) { log_error_write(srv, __FILE__, __LINE__, "sbss", "opening plain-file", fn, "failed", strerror(errno)); - + close(ofd); - + + /* Remove the incomplete cache file, so that later hits aren't served from it */ + if (-1 == unlink(p->ofn->ptr)) { + log_error_write(srv, __FILE__, __LINE__, "sbss", "unlinking incomplete cachefile", p->ofn, "failed:", strerror(errno)); + } + return -1; } - - + +#ifdef USE_MMAP if (MAP_FAILED == (start = mmap(NULL, sce->st.st_size, PROT_READ, MAP_SHARED, ifd, 0))) { log_error_write(srv, __FILE__, __LINE__, "sbss", "mmaping", fn, "failed", strerror(errno)); - + close(ofd); close(ifd); + + /* Remove the incomplete cache file, so that later hits aren't served from it */ + if (-1 == unlink(p->ofn->ptr)) { + log_error_write(srv, __FILE__, __LINE__, "sbss", "unlinking incomplete cachefile", p->ofn, "failed:", strerror(errno)); + } + return -1; } - +#else + start = malloc(sce->st.st_size); + if (NULL == start || sce->st.st_size != read(ifd, start, sce->st.st_size)) { + log_error_write(srv, __FILE__, __LINE__, "sbss", "reading", fn, "failed", strerror(errno)); + + close(ofd); + close(ifd); + free(start); + + /* Remove the incomplete cache file, so that later hits aren't served from it */ + if (-1 == unlink(p->ofn->ptr)) { + log_error_write(srv, __FILE__, __LINE__, "sbss", "unlinking incomplete cachefile", p->ofn, "failed:", strerror(errno)); + } + + return -1; + } +#endif + switch(type) { #ifdef USE_ZLIB - case HTTP_ACCEPT_ENCODING_GZIP: + case HTTP_ACCEPT_ENCODING_GZIP: + case HTTP_ACCEPT_ENCODING_X_GZIP: ret = deflate_file_to_buffer_gzip(srv, con, p, start, sce->st.st_size, sce->st.st_mtime); break; - case HTTP_ACCEPT_ENCODING_DEFLATE: + case HTTP_ACCEPT_ENCODING_DEFLATE: ret = deflate_file_to_buffer_deflate(srv, con, p, start, sce->st.st_size); break; #endif #ifdef USE_BZ2LIB - case HTTP_ACCEPT_ENCODING_BZIP2: + case HTTP_ACCEPT_ENCODING_BZIP2: + case HTTP_ACCEPT_ENCODING_X_BZIP2: ret = deflate_file_to_buffer_bzip2(srv, con, p, start, sce->st.st_size); break; #endif @@ -437,26 +545,38 @@ static int deflate_file_to_file(server *srv, connection *con, plugin_data *p, bu ret = -1; break; } - - if (-1 == (r = write(ofd, p->b->ptr, p->b->used))) { - munmap(start, sce->st.st_size); - close(ofd); - close(ifd); - return -1; - } - - if ((size_t)r != p->b->used) { - + + if (ret == 0) { + r = write(ofd, p->b->ptr, p->b->used); + if (-1 == r) { + log_error_write(srv, __FILE__, __LINE__, "sbss", "writing cachefile", p->ofn, "failed:", strerror(errno)); + ret = -1; + } else if ((size_t)r != p->b->used) { + log_error_write(srv, __FILE__, __LINE__, "sbs", "writing cachefile", p->ofn, "failed: not enough bytes written"); + ret = -1; + } } - + +#ifdef USE_MMAP munmap(start, sce->st.st_size); +#else + free(start); +#endif + close(ofd); close(ifd); - - if (ret != 0) return -1; - + + if (ret != 0) { + /* Remove the incomplete cache file, so that later hits aren't served from it */ + if (-1 == unlink(p->ofn->ptr)) { + log_error_write(srv, __FILE__, __LINE__, "sbss", "unlinking incomplete cachefile", p->ofn, "failed:", strerror(errno)); + } + + return -1; + } + buffer_copy_string_buffer(con->physical.path, p->ofn); - + return 0; } @@ -465,43 +585,55 @@ static int deflate_file_to_buffer(server *srv, connection *con, plugin_data *p, int ret = -1; void *start; buffer *b; - + /* overflow */ if ((off_t)(sce->st.st_size * 1.1) < sce->st.st_size) return -1; - + /* don't mmap files > 128M - * + * * we could use a sliding window, but currently there is no need for it */ - + if (sce->st.st_size > 128 * 1024 * 1024) return -1; - - + + if (-1 == (ifd = open(fn->ptr, O_RDONLY | O_BINARY))) { log_error_write(srv, __FILE__, __LINE__, "sbss", "opening plain-file", fn, "failed", strerror(errno)); - + return -1; } - - + +#ifdef USE_MMAP if (MAP_FAILED == (start = mmap(NULL, sce->st.st_size, PROT_READ, MAP_SHARED, ifd, 0))) { log_error_write(srv, __FILE__, __LINE__, "sbss", "mmaping", fn, "failed", strerror(errno)); - + + close(ifd); + return -1; + } +#else + start = malloc(sce->st.st_size); + if (NULL == start || sce->st.st_size != read(ifd, start, sce->st.st_size)) { + log_error_write(srv, __FILE__, __LINE__, "sbss", "reading", fn, "failed", strerror(errno)); + close(ifd); + free(start); return -1; } - +#endif + switch(type) { #ifdef USE_ZLIB - case HTTP_ACCEPT_ENCODING_GZIP: + case HTTP_ACCEPT_ENCODING_GZIP: + case HTTP_ACCEPT_ENCODING_X_GZIP: ret = deflate_file_to_buffer_gzip(srv, con, p, start, sce->st.st_size, sce->st.st_mtime); break; - case HTTP_ACCEPT_ENCODING_DEFLATE: + case HTTP_ACCEPT_ENCODING_DEFLATE: ret = deflate_file_to_buffer_deflate(srv, con, p, start, sce->st.st_size); break; #endif #ifdef USE_BZ2LIB - case HTTP_ACCEPT_ENCODING_BZIP2: + case HTTP_ACCEPT_ENCODING_BZIP2: + case HTTP_ACCEPT_ENCODING_X_BZIP2: ret = deflate_file_to_buffer_bzip2(srv, con, p, start, sce->st.st_size); break; #endif @@ -509,21 +641,25 @@ static int deflate_file_to_buffer(server *srv, connection *con, plugin_data *p, ret = -1; break; } - + +#ifdef USE_MMAP munmap(start, sce->st.st_size); +#else + free(start); +#endif close(ifd); - + if (ret != 0) return -1; - + chunkqueue_reset(con->write_queue); b = chunkqueue_get_append_buffer(con->write_queue); buffer_copy_memory(b, p->b->ptr, p->b->used + 1); - + buffer_reset(con->physical.path); - + con->file_finished = 1; con->file_started = 1; - + return 0; } @@ -537,41 +673,62 @@ static int mod_compress_patch_connection(server *srv, connection *con, plugin_da PATCH(compress_cache_dir); PATCH(compress); PATCH(compress_max_filesize); - + PATCH(allowed_encodings); + /* skip the first, the global context */ for (i = 1; i < srv->config_context->used; i++) { data_config *dc = (data_config *)srv->config_context->data[i]; s = p->config_storage[i]; - + /* condition didn't match */ if (!config_check_cond(srv, con, dc)) continue; - + /* merge config */ for (j = 0; j < dc->value->used; j++) { data_unset *du = dc->value->data[j]; - + if (buffer_is_equal_string(du->key, CONST_STR_LEN("compress.cache-dir"))) { PATCH(compress_cache_dir); } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("compress.filetype"))) { PATCH(compress); } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("compress.max-filesize"))) { PATCH(compress_max_filesize); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("compress.allowed-encodings"))) { + PATCH(allowed_encodings); } } } - + return 0; } #undef PATCH +static int mod_compress_contains_encoding(const char *headervalue, const char *encoding) { + const char *m; + for ( ;; ) { + m = strstr(headervalue, encoding); + if (NULL == m) return 0; + if (m == headervalue || m[-1] == ' ' || m[-1] == ',') return 1; + + /* only partial match, search for next value */ + m = strchr(m, ','); + if (NULL == m) return 0; + headervalue = m + 1; + } +} + PHYSICALPATH_FUNC(mod_compress_physical) { plugin_data *p = p_d; size_t m; off_t max_fsize; stat_cache_entry *sce = NULL; - + buffer *mtime = NULL; + buffer *content_type; + + if (con->mode != DIRECT || con->http_status) return HANDLER_GO_ON; + /* only GET and POST can get compressed */ - if (con->request.http_method != HTTP_METHOD_GET && + if (con->request.http_method != HTTP_METHOD_GET && con->request.http_method != HTTP_METHOD_POST) { return HANDLER_GO_ON; } @@ -579,123 +736,188 @@ PHYSICALPATH_FUNC(mod_compress_physical) { if (buffer_is_empty(con->physical.path)) { return HANDLER_GO_ON; } - + mod_compress_patch_connection(srv, con, p); - + max_fsize = p->conf.compress_max_filesize; - stat_cache_get_entry(srv, con, con->physical.path, &sce); + if (con->conf.log_request_handling) { + log_error_write(srv, __FILE__, __LINE__, "s", "-- handling file as static file"); + } + + if (HANDLER_ERROR == stat_cache_get_entry(srv, con, con->physical.path, &sce)) { + con->http_status = 403; + + log_error_write(srv, __FILE__, __LINE__, "sbsb", + "not a regular file:", con->uri.path, + "->", con->physical.path); + + return HANDLER_FINISHED; + } + + /* we only handle regular files */ +#ifdef HAVE_LSTAT + if ((sce->is_symlink == 1) && !con->conf.follow_symlink) { + return HANDLER_GO_ON; + } +#endif + if (!S_ISREG(sce->st.st_mode)) { + return HANDLER_GO_ON; + } /* don't compress files that are too large as we need to much time to handle them */ if (max_fsize && (sce->st.st_size >> 10) > max_fsize) return HANDLER_GO_ON; - + + /* don't try to compress files less than 128 bytes + * + * - extra overhead for compression + * - mmap() fails for st_size = 0 :) + */ + if (sce->st.st_size < 128) return HANDLER_GO_ON; + /* check if mimetype is in compress-config */ + content_type = NULL; + if (sce->content_type->ptr) { + char *c; + if ( (c = strchr(sce->content_type->ptr, ';')) != NULL) { + content_type = srv->tmp_buf; + buffer_copy_string_len(content_type, sce->content_type->ptr, c - sce->content_type->ptr); + } + } + for (m = 0; m < p->conf.compress->used; m++) { data_string *compress_ds = (data_string *)p->conf.compress->data[m]; - + if (!compress_ds) { log_error_write(srv, __FILE__, __LINE__, "sbb", "evil", con->physical.path, con->uri.path); - + return HANDLER_GO_ON; } - - if (buffer_is_equal(compress_ds->value, sce->content_type)) { + + if (buffer_is_equal(compress_ds->value, sce->content_type) + || (content_type && buffer_is_equal(compress_ds->value, content_type))) { /* mimetype found */ data_string *ds; - + /* the response might change according to Accept-Encoding */ response_header_insert(srv, con, CONST_STR_LEN("Vary"), CONST_STR_LEN("Accept-Encoding")); - + if (NULL != (ds = (data_string *)array_get_element(con->request.headers, "Accept-Encoding"))) { int accept_encoding = 0; char *value = ds->value->ptr; - int srv_encodings = 0; int matched_encodings = 0; - + int use_etag = sce->etag != NULL && sce->etag->ptr != NULL; + /* get client side support encodings */ - if (NULL != strstr(value, "gzip")) accept_encoding |= HTTP_ACCEPT_ENCODING_GZIP; - if (NULL != strstr(value, "deflate")) accept_encoding |= HTTP_ACCEPT_ENCODING_DEFLATE; - if (NULL != strstr(value, "compress")) accept_encoding |= HTTP_ACCEPT_ENCODING_COMPRESS; - if (NULL != strstr(value, "bzip2")) accept_encoding |= HTTP_ACCEPT_ENCODING_BZIP2; - if (NULL != strstr(value, "identity")) accept_encoding |= HTTP_ACCEPT_ENCODING_IDENTITY; - - /* get server side supported ones */ -#ifdef USE_BZ2LIB - srv_encodings |= HTTP_ACCEPT_ENCODING_BZIP2; -#endif #ifdef USE_ZLIB - srv_encodings |= HTTP_ACCEPT_ENCODING_GZIP; - srv_encodings |= HTTP_ACCEPT_ENCODING_DEFLATE; + if (mod_compress_contains_encoding(value, "gzip")) accept_encoding |= HTTP_ACCEPT_ENCODING_GZIP; + if (mod_compress_contains_encoding(value, "x-gzip")) accept_encoding |= HTTP_ACCEPT_ENCODING_X_GZIP; + if (mod_compress_contains_encoding(value, "deflate")) accept_encoding |= HTTP_ACCEPT_ENCODING_DEFLATE; + if (mod_compress_contains_encoding(value, "compress")) accept_encoding |= HTTP_ACCEPT_ENCODING_COMPRESS; #endif - +#ifdef USE_BZ2LIB + if (mod_compress_contains_encoding(value, "bzip2")) accept_encoding |= HTTP_ACCEPT_ENCODING_BZIP2; + if (mod_compress_contains_encoding(value, "x-bzip2")) accept_encoding |= HTTP_ACCEPT_ENCODING_X_BZIP2; +#endif + if (mod_compress_contains_encoding(value, "identity")) accept_encoding |= HTTP_ACCEPT_ENCODING_IDENTITY; + /* find matching entries */ - matched_encodings = accept_encoding & srv_encodings; - + matched_encodings = accept_encoding & p->conf.allowed_encodings; + if (matched_encodings) { - const char *dflt_gzip = "gzip"; - const char *dflt_deflate = "deflate"; - const char *dflt_bzip2 = "bzip2"; - + static const char dflt_gzip[] = "gzip"; + static const char dflt_x_gzip[] = "x-gzip"; + static const char dflt_deflate[] = "deflate"; + static const char dflt_bzip2[] = "bzip2"; + static const char dflt_x_bzip2[] = "x-bzip2"; + const char *compression_name = NULL; int compression_type = 0; - + + mtime = strftime_cache_get(srv, sce->st.st_mtime); + + /* try matching original etag of uncompressed version */ + if (use_etag) { + etag_mutate(con->physical.etag, sce->etag); + if (HANDLER_FINISHED == http_response_handle_cachable(srv, con, mtime)) { + response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_BUF_LEN(sce->content_type)); + response_header_overwrite(srv, con, CONST_STR_LEN("Last-Modified"), CONST_BUF_LEN(mtime)); + response_header_overwrite(srv, con, CONST_STR_LEN("ETag"), CONST_BUF_LEN(con->physical.etag)); + return HANDLER_FINISHED; + } + } + /* select best matching encoding */ if (matched_encodings & HTTP_ACCEPT_ENCODING_BZIP2) { compression_type = HTTP_ACCEPT_ENCODING_BZIP2; compression_name = dflt_bzip2; + } else if (matched_encodings & HTTP_ACCEPT_ENCODING_X_BZIP2) { + compression_type = HTTP_ACCEPT_ENCODING_X_BZIP2; + compression_name = dflt_x_bzip2; } else if (matched_encodings & HTTP_ACCEPT_ENCODING_GZIP) { compression_type = HTTP_ACCEPT_ENCODING_GZIP; compression_name = dflt_gzip; + } else if (matched_encodings & HTTP_ACCEPT_ENCODING_X_GZIP) { + compression_type = HTTP_ACCEPT_ENCODING_X_GZIP; + compression_name = dflt_x_gzip; } else if (matched_encodings & HTTP_ACCEPT_ENCODING_DEFLATE) { compression_type = HTTP_ACCEPT_ENCODING_DEFLATE; compression_name = dflt_deflate; } - - /* deflate it */ - if (p->conf.compress_cache_dir->used) { - if (0 == deflate_file_to_file(srv, con, p, - con->physical.path, sce, compression_type)) { - buffer *mtime; - - response_header_overwrite(srv, con, CONST_STR_LEN("Content-Encoding"), compression_name, strlen(compression_name)); - - mtime = strftime_cache_get(srv, sce->st.st_mtime); - response_header_overwrite(srv, con, CONST_STR_LEN("Last-Modified"), CONST_BUF_LEN(mtime)); - - etag_mutate(con->physical.etag, sce->etag); - response_header_overwrite(srv, con, CONST_STR_LEN("ETag"), CONST_BUF_LEN(con->physical.etag)); - response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_BUF_LEN(sce->content_type)); + if (use_etag) { + /* try matching etag of compressed version */ + buffer_copy_string_buffer(srv->tmp_buf, sce->etag); + buffer_append_string_len(srv->tmp_buf, CONST_STR_LEN("-")); + buffer_append_string(srv->tmp_buf, compression_name); + etag_mutate(con->physical.etag, srv->tmp_buf); + } - return HANDLER_GO_ON; - } - } else if (0 == deflate_file_to_buffer(srv, con, p, - con->physical.path, sce, compression_type)) { - + if (HANDLER_FINISHED == http_response_handle_cachable(srv, con, mtime)) { response_header_overwrite(srv, con, CONST_STR_LEN("Content-Encoding"), compression_name, strlen(compression_name)); response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_BUF_LEN(sce->content_type)); - + response_header_overwrite(srv, con, CONST_STR_LEN("Last-Modified"), CONST_BUF_LEN(mtime)); + if (use_etag) { + response_header_overwrite(srv, con, CONST_STR_LEN("ETag"), CONST_BUF_LEN(con->physical.etag)); + } return HANDLER_FINISHED; } - break; + + /* deflate it */ + if (use_etag && p->conf.compress_cache_dir->used) { + if (0 != deflate_file_to_file(srv, con, p, con->physical.path, sce, compression_type)) + return HANDLER_GO_ON; + } else { + if (0 != deflate_file_to_buffer(srv, con, p, con->physical.path, sce, compression_type)) + return HANDLER_GO_ON; + } + response_header_overwrite(srv, con, CONST_STR_LEN("Content-Encoding"), compression_name, strlen(compression_name)); + response_header_overwrite(srv, con, CONST_STR_LEN("Last-Modified"), CONST_BUF_LEN(mtime)); + if (use_etag) { + response_header_overwrite(srv, con, CONST_STR_LEN("ETag"), CONST_BUF_LEN(con->physical.etag)); + } + response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_BUF_LEN(sce->content_type)); + /* let mod_staticfile handle the cached compressed files, physical path was modified */ + return (use_etag && p->conf.compress_cache_dir->used) ? HANDLER_GO_ON : HANDLER_FINISHED; } } } } - + return HANDLER_GO_ON; } +int mod_compress_plugin_init(plugin *p); int mod_compress_plugin_init(plugin *p) { p->version = LIGHTTPD_VERSION_ID; p->name = buffer_init_string("compress"); - + p->init = mod_compress_init; p->set_defaults = mod_compress_setdefaults; p->handle_subrequest_start = mod_compress_physical; p->cleanup = mod_compress_free; - + p->data = NULL; - + return 0; } diff --git a/src/mod_dirlisting.c b/src/mod_dirlisting.c index 69eb1e9..cd317ec 100644 --- a/src/mod_dirlisting.c +++ b/src/mod_dirlisting.c @@ -1,13 +1,3 @@ -#include <ctype.h> -#include <stdlib.h> -#include <string.h> -#include <dirent.h> -#include <assert.h> -#include <errno.h> -#include <stdio.h> -#include <unistd.h> -#include <time.h> - #include "base.h" #include "log.h" #include "buffer.h" @@ -18,6 +8,16 @@ #include "stat_cache.h" #include "stream.h" +#include <ctype.h> +#include <stdlib.h> +#include <string.h> +#include <dirent.h> +#include <assert.h> +#include <errno.h> +#include <stdio.h> +#include <unistd.h> +#include <time.h> + /** * this is a dirlisting for a lighttpd plugin */ @@ -31,6 +31,8 @@ #include <attr/attributes.h> #endif +#include "version.h" + /* plugin config for all request/connections */ typedef struct { @@ -52,27 +54,31 @@ typedef struct { unsigned short hide_dot_files; unsigned short show_readme; unsigned short hide_readme_file; + unsigned short encode_readme; unsigned short show_header; unsigned short hide_header_file; - + unsigned short encode_header; + unsigned short auto_layout; + excludes_buffer *excludes; buffer *external_css; buffer *encoding; + buffer *set_footer; } plugin_config; typedef struct { PLUGIN_DATA; - + buffer *tmp_buf; buffer *content_charset; - + plugin_config **config_storage; - - plugin_config conf; + + plugin_config conf; } plugin_data; -excludes_buffer *excludes_buffer_init(void) { +static excludes_buffer *excludes_buffer_init(void) { excludes_buffer *exb; exb = calloc(1, sizeof(*exb)); @@ -80,7 +86,7 @@ excludes_buffer *excludes_buffer_init(void) { return exb; } -int excludes_buffer_append(excludes_buffer *exb, buffer *string) { +static int excludes_buffer_append(excludes_buffer *exb, buffer *string) { #ifdef HAVE_PCRE_H size_t i; const char *errptr; @@ -127,7 +133,7 @@ int excludes_buffer_append(excludes_buffer *exb, buffer *string) { #endif } -void excludes_buffer_free(excludes_buffer *exb) { +static void excludes_buffer_free(excludes_buffer *exb) { #ifdef HAVE_PCRE_H size_t i; @@ -146,44 +152,45 @@ void excludes_buffer_free(excludes_buffer *exb) { /* init the plugin data */ INIT_FUNC(mod_dirlisting_init) { plugin_data *p; - + p = calloc(1, sizeof(*p)); p->tmp_buf = buffer_init(); p->content_charset = buffer_init(); - + return p; } /* detroy the plugin data */ FREE_FUNC(mod_dirlisting_free) { plugin_data *p = p_d; - + UNUSED(srv); if (!p) return HANDLER_GO_ON; - + if (p->config_storage) { size_t i; for (i = 0; i < srv->config_context->used; i++) { plugin_config *s = p->config_storage[i]; - + if (!s) continue; - + excludes_buffer_free(s->excludes); buffer_free(s->external_css); buffer_free(s->encoding); - + buffer_free(s->set_footer); + free(s); } free(p->config_storage); } - + buffer_free(p->tmp_buf); buffer_free(p->content_charset); - + free(p); - + return HANDLER_GO_ON; } @@ -191,7 +198,7 @@ static int parse_config_entry(server *srv, plugin_config *s, array *ca, const ch data_unset *du; if (NULL != (du = array_get_element(ca, option))) { - data_array *da = (data_array *)du; + data_array *da; size_t j; if (du->type != TYPE_ARRAY) { @@ -215,10 +222,10 @@ static int parse_config_entry(server *srv, plugin_config *s, array *ca, const ch if (0 != excludes_buffer_append(s->excludes, ((data_string *)(da->value->data[j]))->value)) { #ifdef HAVE_PCRE_H - log_error_write(srv, __FILE__, __LINE__, "sb", + log_error_write(srv, __FILE__, __LINE__, "sb", "pcre-compile failed for", ((data_string *)(da->value->data[j]))->value); #else - log_error_write(srv, __FILE__, __LINE__, "s", + log_error_write(srv, __FILE__, __LINE__, "s", "pcre support is missing, please install libpcre and the headers"); #endif } @@ -230,33 +237,53 @@ static int parse_config_entry(server *srv, plugin_config *s, array *ca, const ch /* handle plugin config and check values */ +#define CONFIG_EXCLUDE "dir-listing.exclude" +#define CONFIG_ACTIVATE "dir-listing.activate" +#define CONFIG_HIDE_DOTFILES "dir-listing.hide-dotfiles" +#define CONFIG_EXTERNAL_CSS "dir-listing.external-css" +#define CONFIG_ENCODING "dir-listing.encoding" +#define CONFIG_SHOW_README "dir-listing.show-readme" +#define CONFIG_HIDE_README_FILE "dir-listing.hide-readme-file" +#define CONFIG_SHOW_HEADER "dir-listing.show-header" +#define CONFIG_HIDE_HEADER_FILE "dir-listing.hide-header-file" +#define CONFIG_DIR_LISTING "server.dir-listing" +#define CONFIG_SET_FOOTER "dir-listing.set-footer" +#define CONFIG_ENCODE_README "dir-listing.encode-readme" +#define CONFIG_ENCODE_HEADER "dir-listing.encode-header" +#define CONFIG_AUTO_LAYOUT "dir-listing.auto-layout" + + SETDEFAULTS_FUNC(mod_dirlisting_set_defaults) { plugin_data *p = p_d; size_t i = 0; - - config_values_t cv[] = { - { "dir-listing.exclude", NULL, T_CONFIG_LOCAL, T_CONFIG_SCOPE_CONNECTION }, /* 0 */ - { "dir-listing.activate", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 1 */ - { "dir-listing.hide-dotfiles", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 2 */ - { "dir-listing.external-css", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 3 */ - { "dir-listing.encoding", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 4 */ - { "dir-listing.show-readme", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 5 */ - { "dir-listing.hide-readme-file", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 6 */ - { "dir-listing.show-header", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 7 */ - { "dir-listing.hide-header-file", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 8 */ - { "server.dir-listing", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 9 */ - + + config_values_t cv[] = { + { CONFIG_EXCLUDE, NULL, T_CONFIG_LOCAL, T_CONFIG_SCOPE_CONNECTION }, /* 0 */ + { CONFIG_ACTIVATE, NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 1 */ + { CONFIG_HIDE_DOTFILES, NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 2 */ + { CONFIG_EXTERNAL_CSS, NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 3 */ + { CONFIG_ENCODING, NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 4 */ + { CONFIG_SHOW_README, NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 5 */ + { CONFIG_HIDE_README_FILE, NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 6 */ + { CONFIG_SHOW_HEADER, NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 7 */ + { CONFIG_HIDE_HEADER_FILE, NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 8 */ + { CONFIG_DIR_LISTING, NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 9 */ + { CONFIG_SET_FOOTER, NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 10 */ + { CONFIG_ENCODE_README, NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 11 */ + { CONFIG_ENCODE_HEADER, NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 12 */ + { CONFIG_AUTO_LAYOUT, NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 13 */ + { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET } }; - + if (!p) return HANDLER_ERROR; - + p->config_storage = calloc(1, srv->config_context->used * sizeof(specific_config *)); - + for (i = 0; i < srv->config_context->used; i++) { plugin_config *s; array *ca; - + s = calloc(1, sizeof(plugin_config)); s->excludes = excludes_buffer_init(); s->dir_listing = 0; @@ -266,8 +293,13 @@ SETDEFAULTS_FUNC(mod_dirlisting_set_defaults) { s->hide_readme_file = 0; s->show_header = 0; s->hide_header_file = 0; + s->encode_readme = 1; + s->encode_header = 1; + s->auto_layout = 1; + s->encoding = buffer_init(); - + s->set_footer = buffer_init(); + cv[0].destination = s->excludes; cv[1].destination = &(s->dir_listing); cv[2].destination = &(s->hide_dot_files); @@ -278,6 +310,10 @@ SETDEFAULTS_FUNC(mod_dirlisting_set_defaults) { cv[7].destination = &(s->show_header); cv[8].destination = &(s->hide_header_file); cv[9].destination = &(s->dir_listing); /* old name */ + cv[10].destination = s->set_footer; + cv[11].destination = &(s->encode_readme); + cv[12].destination = &(s->encode_header); + cv[13].destination = &(s->auto_layout); p->config_storage[i] = s; ca = ((data_config *)srv->config_context->data[i])->value; @@ -286,7 +322,7 @@ SETDEFAULTS_FUNC(mod_dirlisting_set_defaults) { return HANDLER_ERROR; } - parse_config_entry(srv, s, ca, "dir-listing.exclude"); + parse_config_entry(srv, s, ca, CONFIG_EXCLUDE); } return HANDLER_GO_ON; @@ -307,42 +343,54 @@ static int mod_dirlisting_patch_connection(server *srv, connection *con, plugin_ PATCH(show_header); PATCH(hide_header_file); PATCH(excludes); - + PATCH(set_footer); + PATCH(encode_readme); + PATCH(encode_header); + PATCH(auto_layout); + /* skip the first, the global context */ for (i = 1; i < srv->config_context->used; i++) { data_config *dc = (data_config *)srv->config_context->data[i]; s = p->config_storage[i]; - + /* condition didn't match */ if (!config_check_cond(srv, con, dc)) continue; - + /* merge config */ for (j = 0; j < dc->value->used; j++) { data_unset *du = dc->value->data[j]; - - if (buffer_is_equal_string(du->key, CONST_STR_LEN("dir-listing.activate")) || - buffer_is_equal_string(du->key, CONST_STR_LEN("server.dir-listing"))) { + + if (buffer_is_equal_string(du->key, CONST_STR_LEN(CONFIG_ACTIVATE)) || + buffer_is_equal_string(du->key, CONST_STR_LEN(CONFIG_DIR_LISTING))) { PATCH(dir_listing); - } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("dir-listing.hide-dotfiles"))) { + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN(CONFIG_HIDE_DOTFILES))) { PATCH(hide_dot_files); - } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("dir-listing.external-css"))) { + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN(CONFIG_EXTERNAL_CSS))) { PATCH(external_css); - } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("dir-listing.encoding"))) { + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN(CONFIG_ENCODING))) { PATCH(encoding); - } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("dir-listing.show-readme"))) { + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN(CONFIG_SHOW_README))) { PATCH(show_readme); - } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("dir-listing.hide-readme-file"))) { + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN(CONFIG_HIDE_README_FILE))) { PATCH(hide_readme_file); - } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("dir-listing.show-header"))) { + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN(CONFIG_SHOW_HEADER))) { PATCH(show_header); - } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("dir-listing.hide-header-file"))) { + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN(CONFIG_HIDE_HEADER_FILE))) { PATCH(hide_header_file); - } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("dir-listing.excludes"))) { + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN(CONFIG_SET_FOOTER))) { + PATCH(set_footer); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN(CONFIG_EXCLUDE))) { PATCH(excludes); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN(CONFIG_ENCODE_README))) { + PATCH(encode_readme); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN(CONFIG_ENCODE_HEADER))) { + PATCH(encode_header); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN(CONFIG_AUTO_LAYOUT))) { + PATCH(auto_layout); } } } - + return 0; } #undef PATCH @@ -421,7 +469,7 @@ static int http_list_directory_sizefmt(char *buf, off_t size) { u++; } - out += ltostr(out, size); + out += LI_ltostr(out, size); out[0] = '.'; out[1] = remain + '0'; out[2] = *u; @@ -432,85 +480,87 @@ static int http_list_directory_sizefmt(char *buf, off_t size) { static void http_list_directory_header(server *srv, connection *con, plugin_data *p, buffer *out) { UNUSED(srv); - - BUFFER_APPEND_STRING_CONST(out, - "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.1//EN\" \"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd\">\n" - "<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\">\n" - "<head>\n" - "<title>Index of " - ); - buffer_append_string_encoded(out, CONST_BUF_LEN(con->uri.path), ENCODING_MINIMAL_XML); - BUFFER_APPEND_STRING_CONST(out, "</title>\n"); - if (p->conf.external_css->used > 1) { - BUFFER_APPEND_STRING_CONST(out, "<link rel=\"stylesheet\" type=\"text/css\" href=\""); - buffer_append_string_buffer(out, p->conf.external_css); - BUFFER_APPEND_STRING_CONST(out, "\" />\n"); - } else { - BUFFER_APPEND_STRING_CONST(out, - "<style type=\"text/css\">\n" - "a, a:active {text-decoration: none; color: blue;}\n" - "a:visited {color: #48468F;}\n" - "a:hover, a:focus {text-decoration: underline; color: red;}\n" - "body {background-color: #F5F5F5;}\n" - "h2 {margin-bottom: 12px;}\n" - "table {margin-left: 12px;}\n" - "th, td {" - " font-family: \"Courier New\", Courier, monospace;" - " font-size: 10pt;" - " text-align: left;" - "}\n" - "th {" - " font-weight: bold;" - " padding-right: 14px;" - " padding-bottom: 3px;" - "}\n" - ); - BUFFER_APPEND_STRING_CONST(out, - "td {padding-right: 14px;}\n" - "td.s, th.s {text-align: right;}\n" - "div.list {" - " background-color: white;" - " border-top: 1px solid #646464;" - " border-bottom: 1px solid #646464;" - " padding-top: 10px;" - " padding-bottom: 14px;" - "}\n" - "div.foot {" - " font-family: \"Courier New\", Courier, monospace;" - " font-size: 10pt;" - " color: #787878;" - " padding-top: 4px;" - "}\n" - "</style>\n" - ); - } + if (p->conf.auto_layout) { + buffer_append_string_len(out, CONST_STR_LEN( + "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.1//EN\" \"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd\">\n" + "<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\">\n" + "<head>\n" + "<title>Index of " + )); + buffer_append_string_encoded(out, CONST_BUF_LEN(con->uri.path), ENCODING_MINIMAL_XML); + buffer_append_string_len(out, CONST_STR_LEN("</title>\n")); + + if (p->conf.external_css->used > 1) { + buffer_append_string_len(out, CONST_STR_LEN("<link rel=\"stylesheet\" type=\"text/css\" href=\"")); + buffer_append_string_buffer(out, p->conf.external_css); + buffer_append_string_len(out, CONST_STR_LEN("\" />\n")); + } else { + buffer_append_string_len(out, CONST_STR_LEN( + "<style type=\"text/css\">\n" + "a, a:active {text-decoration: none; color: blue;}\n" + "a:visited {color: #48468F;}\n" + "a:hover, a:focus {text-decoration: underline; color: red;}\n" + "body {background-color: #F5F5F5;}\n" + "h2 {margin-bottom: 12px;}\n" + "table {margin-left: 12px;}\n" + "th, td {" + " font: 90% monospace;" + " text-align: left;" + "}\n" + "th {" + " font-weight: bold;" + " padding-right: 14px;" + " padding-bottom: 3px;" + "}\n" + "td {padding-right: 14px;}\n" + "td.s, th.s {text-align: right;}\n" + "div.list {" + " background-color: white;" + " border-top: 1px solid #646464;" + " border-bottom: 1px solid #646464;" + " padding-top: 10px;" + " padding-bottom: 14px;" + "}\n" + "div.foot {" + " font: 90% monospace;" + " color: #787878;" + " padding-top: 4px;" + "}\n" + "</style>\n" + )); + } - BUFFER_APPEND_STRING_CONST(out, "</head>\n<body>\n"); + buffer_append_string_len(out, CONST_STR_LEN("</head>\n<body>\n")); + } /* HEADER.txt */ if (p->conf.show_header) { stream s; /* if we have a HEADER file, display it in <pre class="header"></pre> */ - + buffer_copy_string_buffer(p->tmp_buf, con->physical.path); BUFFER_APPEND_SLASH(p->tmp_buf); - BUFFER_APPEND_STRING_CONST(p->tmp_buf, "HEADER.txt"); - + buffer_append_string_len(p->tmp_buf, CONST_STR_LEN("HEADER.txt")); + if (-1 != stream_open(&s, p->tmp_buf)) { - BUFFER_APPEND_STRING_CONST(out, "<pre class=\"header\">"); - buffer_append_string_encoded(out, s.start, s.size, ENCODING_MINIMAL_XML); - BUFFER_APPEND_STRING_CONST(out, "</pre>"); + if (p->conf.encode_header) { + buffer_append_string_len(out, CONST_STR_LEN("<pre class=\"header\">")); + buffer_append_string_encoded(out, s.start, s.size, ENCODING_MINIMAL_XML); + buffer_append_string_len(out, CONST_STR_LEN("</pre>")); + } else { + buffer_append_string_len(out, s.start, s.size); + } } stream_close(&s); } - BUFFER_APPEND_STRING_CONST(out, "<h2>Index of "); + buffer_append_string_len(out, CONST_STR_LEN("<h2>Index of ")); buffer_append_string_encoded(out, CONST_BUF_LEN(con->uri.path), ENCODING_MINIMAL_XML); - BUFFER_APPEND_STRING_CONST(out, + buffer_append_string_len(out, CONST_STR_LEN( "</h2>\n" "<div class=\"list\">\n" - "<table cellpadding=\"0\" cellspacing=\"0\">\n" + "<table summary=\"Directory Listing\" cellpadding=\"0\" cellspacing=\"0\">\n" "<thead>" "<tr>" "<th class=\"n\">Name</th>" @@ -526,49 +576,57 @@ static void http_list_directory_header(server *srv, connection *con, plugin_data "<td class=\"s\">- </td>" "<td class=\"t\">Directory</td>" "</tr>\n" - ); + )); } static void http_list_directory_footer(server *srv, connection *con, plugin_data *p, buffer *out) { UNUSED(srv); - - BUFFER_APPEND_STRING_CONST(out, + + buffer_append_string_len(out, CONST_STR_LEN( "</tbody>\n" "</table>\n" "</div>\n" - ); - + )); + if (p->conf.show_readme) { stream s; /* if we have a README file, display it in <pre class="readme"></pre> */ - + buffer_copy_string_buffer(p->tmp_buf, con->physical.path); BUFFER_APPEND_SLASH(p->tmp_buf); - BUFFER_APPEND_STRING_CONST(p->tmp_buf, "README.txt"); - + buffer_append_string_len(p->tmp_buf, CONST_STR_LEN("README.txt")); + if (-1 != stream_open(&s, p->tmp_buf)) { - BUFFER_APPEND_STRING_CONST(out, "<pre class=\"readme\">"); - buffer_append_string_encoded(out, s.start, s.size, ENCODING_MINIMAL_XML); - BUFFER_APPEND_STRING_CONST(out, "</pre>"); + if (p->conf.encode_readme) { + buffer_append_string_len(out, CONST_STR_LEN("<pre class=\"readme\">")); + buffer_append_string_encoded(out, s.start, s.size, ENCODING_MINIMAL_XML); + buffer_append_string_len(out, CONST_STR_LEN("</pre>")); + } else { + buffer_append_string_len(out, s.start, s.size); + } } stream_close(&s); } - - BUFFER_APPEND_STRING_CONST(out, - "<div class=\"foot\">" - ); - if (buffer_is_empty(con->conf.server_tag)) { - BUFFER_APPEND_STRING_CONST(out, PACKAGE_NAME "/" PACKAGE_VERSION); - } else { - buffer_append_string_buffer(out, con->conf.server_tag); - } + if(p->conf.auto_layout) { + buffer_append_string_len(out, CONST_STR_LEN( + "<div class=\"foot\">" + )); + + if (p->conf.set_footer->used > 1) { + buffer_append_string_buffer(out, p->conf.set_footer); + } else if (buffer_is_empty(con->conf.server_tag)) { + buffer_append_string_len(out, CONST_STR_LEN(PACKAGE_DESC)); + } else { + buffer_append_string_buffer(out, con->conf.server_tag); + } - BUFFER_APPEND_STRING_CONST(out, - "</div>\n" - "</body>\n" - "</html>\n" - ); + buffer_append_string_len(out, CONST_STR_LEN( + "</div>\n" + "</body>\n" + "</html>\n" + )); + } } static int http_list_directory(server *srv, connection *con, plugin_data *p, buffer *dir) { @@ -595,15 +653,16 @@ static int http_list_directory(server *srv, connection *con, plugin_data *p, buf #endif if (dir->used == 0) return -1; - + i = dir->used - 1; #ifdef HAVE_PATHCONF - if (-1 == (name_max = pathconf(dir->ptr, _PC_NAME_MAX))) { + if (0 >= (name_max = pathconf(dir->ptr, _PC_NAME_MAX))) { + /* some broken fs (fuse) return 0 instead of -1 */ #ifdef NAME_MAX name_max = NAME_MAX; #else - name_max = 256; /* stupid default */ + name_max = 255; /* stupid default */ #endif } #elif defined __WIN32 @@ -611,14 +670,14 @@ static int http_list_directory(server *srv, connection *con, plugin_data *p, buf #else name_max = NAME_MAX; #endif - + path = malloc(dir->used + name_max); assert(path); strcpy(path, dir->ptr); path_file = path + i; if (NULL == (dp = opendir(path))) { - log_error_write(srv, __FILE__, __LINE__, "sbs", + log_error_write(srv, __FILE__, __LINE__, "sbs", "opendir failed:", dir, strerror(errno)); free(path); @@ -633,7 +692,7 @@ static int http_list_directory(server *srv, connection *con, plugin_data *p, buf assert(files.ent); files.size = DIRLIST_BLOB_SIZE; files.used = 0; - + while ((dent = readdir(dp)) != NULL) { unsigned short exclude_match = 0; @@ -686,12 +745,12 @@ static int http_list_directory(server *srv, connection *con, plugin_data *p, buf #endif i = strlen(dent->d_name); - + /* NOTE: the manual says, d_name is never more than NAME_MAX * so this should actually not be a buffer-overflow-risk */ if (i > (size_t)name_max) continue; - + memcpy(path_file, dent->d_name, i + 1); if (stat(path, &st) != 0) continue; @@ -721,13 +780,13 @@ static int http_list_directory(server *srv, connection *con, plugin_data *p, buf if (files.used) http_dirls_sort(files.ent, files.used); out = chunkqueue_get_append_buffer(con->write_queue); - BUFFER_COPY_STRING_CONST(out, "<?xml version=\"1.0\" encoding=\""); + buffer_copy_string_len(out, CONST_STR_LEN("<?xml version=\"1.0\" encoding=\"")); if (buffer_is_empty(p->conf.encoding)) { - BUFFER_APPEND_STRING_CONST(out, "iso-8859-1"); + buffer_append_string_len(out, CONST_STR_LEN("iso-8859-1")); } else { buffer_append_string_buffer(out, p->conf.encoding); } - BUFFER_APPEND_STRING_CONST(out, "\"?>\n"); + buffer_append_string_len(out, CONST_STR_LEN("\"?>\n")); http_list_directory_header(srv, con, p, out); /* directories */ @@ -740,14 +799,14 @@ static int http_list_directory(server *srv, connection *con, plugin_data *p, buf #else strftime(datebuf, sizeof(datebuf), "%Y-%b-%d %H:%M:%S", localtime(&(tmp->mtime))); #endif - - BUFFER_APPEND_STRING_CONST(out, "<tr><td class=\"n\"><a href=\""); + + buffer_append_string_len(out, CONST_STR_LEN("<tr><td class=\"n\"><a href=\"")); buffer_append_string_encoded(out, DIRLIST_ENT_NAME(tmp), tmp->namelen, ENCODING_REL_URI_PART); - BUFFER_APPEND_STRING_CONST(out, "/\">"); + buffer_append_string_len(out, CONST_STR_LEN("/\">")); buffer_append_string_encoded(out, DIRLIST_ENT_NAME(tmp), tmp->namelen, ENCODING_MINIMAL_XML); - BUFFER_APPEND_STRING_CONST(out, "</a>/</td><td class=\"m\">"); + buffer_append_string_len(out, CONST_STR_LEN("</a>/</td><td class=\"m\">")); buffer_append_string_len(out, datebuf, sizeof(datebuf) - 1); - BUFFER_APPEND_STRING_CONST(out, "</td><td class=\"s\">- </td><td class=\"t\">Directory</td></tr>\n"); + buffer_append_string_len(out, CONST_STR_LEN("</td><td class=\"s\">- </td><td class=\"t\">Directory</td></tr>\n")); free(tmp); } @@ -758,7 +817,7 @@ static int http_list_directory(server *srv, connection *con, plugin_data *p, buf content_type = NULL; #ifdef HAVE_XATTR - + if (con->conf.use_xattr) { memcpy(path_file, DIRLIST_ENT_NAME(tmp), tmp->namelen + 1); attrlen = sizeof(attrval) - 1; @@ -768,7 +827,7 @@ static int http_list_directory(server *srv, connection *con, plugin_data *p, buf } } #endif - + if (content_type == NULL) { content_type = "application/octet-stream"; for (k = 0; k < con->conf.mimetypes->used; k++) { @@ -788,7 +847,7 @@ static int http_list_directory(server *srv, connection *con, plugin_data *p, buf } } } - + #ifdef HAVE_LOCALTIME_R localtime_r(&(tmp->mtime), &tm); strftime(datebuf, sizeof(datebuf), "%Y-%b-%d %H:%M:%S", &tm); @@ -797,17 +856,17 @@ static int http_list_directory(server *srv, connection *con, plugin_data *p, buf #endif http_list_directory_sizefmt(sizebuf, tmp->size); - BUFFER_APPEND_STRING_CONST(out, "<tr><td class=\"n\"><a href=\""); + buffer_append_string_len(out, CONST_STR_LEN("<tr><td class=\"n\"><a href=\"")); buffer_append_string_encoded(out, DIRLIST_ENT_NAME(tmp), tmp->namelen, ENCODING_REL_URI_PART); - BUFFER_APPEND_STRING_CONST(out, "\">"); + buffer_append_string_len(out, CONST_STR_LEN("\">")); buffer_append_string_encoded(out, DIRLIST_ENT_NAME(tmp), tmp->namelen, ENCODING_MINIMAL_XML); - BUFFER_APPEND_STRING_CONST(out, "</a></td><td class=\"m\">"); + buffer_append_string_len(out, CONST_STR_LEN("</a></td><td class=\"m\">")); buffer_append_string_len(out, datebuf, sizeof(datebuf) - 1); - BUFFER_APPEND_STRING_CONST(out, "</td><td class=\"s\">"); + buffer_append_string_len(out, CONST_STR_LEN("</td><td class=\"s\">")); buffer_append_string(out, sizebuf); - BUFFER_APPEND_STRING_CONST(out, "</td><td class=\"t\">"); + buffer_append_string_len(out, CONST_STR_LEN("</td><td class=\"t\">")); buffer_append_string(out, content_type); - BUFFER_APPEND_STRING_CONST(out, "</td></tr>\n"); + buffer_append_string_len(out, CONST_STR_LEN("</td></tr>\n")); free(tmp); } @@ -820,11 +879,11 @@ static int http_list_directory(server *srv, connection *con, plugin_data *p, buf /* Insert possible charset to Content-Type */ if (buffer_is_empty(p->conf.encoding)) { - response_header_insert(srv, con, CONST_STR_LEN("Content-Type"), CONST_STR_LEN("text/html")); + response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_STR_LEN("text/html")); } else { - buffer_copy_string(p->content_charset, "text/html; charset="); + buffer_copy_string_len(p->content_charset, CONST_STR_LEN("text/html; charset=")); buffer_append_string_buffer(p->content_charset, p->conf.encoding); - response_header_insert(srv, con, CONST_STR_LEN("Content-Type"), CONST_BUF_LEN(p->content_charset)); + response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_BUF_LEN(p->content_charset)); } con->file_finished = 1; @@ -837,52 +896,65 @@ static int http_list_directory(server *srv, connection *con, plugin_data *p, buf URIHANDLER_FUNC(mod_dirlisting_subrequest) { plugin_data *p = p_d; stat_cache_entry *sce = NULL; - + UNUSED(srv); - + + /* we only handle GET, POST and HEAD */ + switch(con->request.http_method) { + case HTTP_METHOD_GET: + case HTTP_METHOD_POST: + case HTTP_METHOD_HEAD: + break; + default: + return HANDLER_GO_ON; + } + + if (con->mode != DIRECT) return HANDLER_GO_ON; + if (con->physical.path->used == 0) return HANDLER_GO_ON; if (con->uri.path->used == 0) return HANDLER_GO_ON; if (con->uri.path->ptr[con->uri.path->used - 2] != '/') return HANDLER_GO_ON; - + mod_dirlisting_patch_connection(srv, con, p); if (!p->conf.dir_listing) return HANDLER_GO_ON; - + if (con->conf.log_request_handling) { log_error_write(srv, __FILE__, __LINE__, "s", "-- handling the request as Dir-Listing"); log_error_write(srv, __FILE__, __LINE__, "sb", "URI :", con->uri.path); } - + if (HANDLER_ERROR == stat_cache_get_entry(srv, con, con->physical.path, &sce)) { - fprintf(stderr, "%s.%d: %s\n", __FILE__, __LINE__, con->physical.path->ptr); + log_error_write(srv, __FILE__, __LINE__, "SB", "stat_cache_get_entry failed: ", con->physical.path); SEGFAULT(); } - + if (!S_ISDIR(sce->st.st_mode)) return HANDLER_GO_ON; - + if (http_list_directory(srv, con, p, con->physical.path)) { /* dirlisting failed */ con->http_status = 403; } - + buffer_reset(con->physical.path); - + /* not found */ return HANDLER_FINISHED; } /* this function is called at dlopen() time and inits the callbacks */ +int mod_dirlisting_plugin_init(plugin *p); int mod_dirlisting_plugin_init(plugin *p) { p->version = LIGHTTPD_VERSION_ID; p->name = buffer_init_string("dirlisting"); - + p->init = mod_dirlisting_init; p->handle_subrequest_start = mod_dirlisting_subrequest; p->set_defaults = mod_dirlisting_set_defaults; p->cleanup = mod_dirlisting_free; - + p->data = NULL; - + return 0; } diff --git a/src/mod_evasive.c b/src/mod_evasive.c index b9d19ca..2907053 100644 --- a/src/mod_evasive.c +++ b/src/mod_evasive.c @@ -1,7 +1,3 @@ -#include <ctype.h> -#include <stdlib.h> -#include <string.h> - #include "base.h" #include "log.h" #include "buffer.h" @@ -10,6 +6,10 @@ #include "inet_ntop_cache.h" +#include <ctype.h> +#include <stdlib.h> +#include <string.h> + /** * mod_evasive * @@ -27,72 +27,76 @@ typedef struct { unsigned short max_conns; + unsigned short silent; } plugin_config; typedef struct { PLUGIN_DATA; - + plugin_config **config_storage; - - plugin_config conf; + + plugin_config conf; } plugin_data; INIT_FUNC(mod_evasive_init) { plugin_data *p; - + p = calloc(1, sizeof(*p)); - + return p; } FREE_FUNC(mod_evasive_free) { plugin_data *p = p_d; - + UNUSED(srv); if (!p) return HANDLER_GO_ON; - + if (p->config_storage) { size_t i; for (i = 0; i < srv->config_context->used; i++) { plugin_config *s = p->config_storage[i]; - + free(s); } free(p->config_storage); } - + free(p); - + return HANDLER_GO_ON; } SETDEFAULTS_FUNC(mod_evasive_set_defaults) { plugin_data *p = p_d; size_t i = 0; - - config_values_t cv[] = { - { "evasive.max-conns-per-ip", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, + + config_values_t cv[] = { + { "evasive.max-conns-per-ip", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 0 */ + { "evasive.silent", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 1 */ { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET } }; - + p->config_storage = calloc(1, srv->config_context->used * sizeof(specific_config *)); - + for (i = 0; i < srv->config_context->used; i++) { plugin_config *s; - + s = calloc(1, sizeof(plugin_config)); s->max_conns = 0; - + s->silent = 0; + cv[0].destination = &(s->max_conns); - + cv[1].destination = &(s->silent); + p->config_storage[i] = s; - + if (0 != config_insert_values_global(srv, ((data_config *)srv->config_context->data[i])->value, cv)) { return HANDLER_ERROR; } } - + return HANDLER_GO_ON; } @@ -103,25 +107,28 @@ static int mod_evasive_patch_connection(server *srv, connection *con, plugin_dat plugin_config *s = p->config_storage[0]; PATCH(max_conns); - + PATCH(silent); + /* skip the first, the global context */ for (i = 1; i < srv->config_context->used; i++) { data_config *dc = (data_config *)srv->config_context->data[i]; s = p->config_storage[i]; - + /* condition didn't match */ if (!config_check_cond(srv, con, dc)) continue; - + /* merge config */ for (j = 0; j < dc->value->used; j++) { data_unset *du = dc->value->data[j]; - + if (buffer_is_equal_string(du->key, CONST_STR_LEN("evasive.max-conns-per-ip"))) { PATCH(max_conns); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("evasive.silent"))) { + PATCH(silent); } } } - + return 0; } #undef PATCH @@ -132,47 +139,73 @@ URIHANDLER_FUNC(mod_evasive_uri_handler) { size_t j; if (con->uri.path->used == 0) return HANDLER_GO_ON; - + mod_evasive_patch_connection(srv, con, p); - - /* no limit set, nothing to block */ + + /* no limit set, nothing to block */ if (p->conf.max_conns == 0) return HANDLER_GO_ON; + switch (con->dst_addr.plain.sa_family) { + case AF_INET: +#ifdef HAVE_IPV6 + case AF_INET6: +#endif + break; + default: /* Address family not supported */ + return HANDLER_GO_ON; + }; + for (j = 0; j < srv->conns->used; j++) { connection *c = srv->conns->ptr[j]; /* check if other connections are already actively serving data for the same IP * we can only ban connections which are already behind the 'read request' state * */ - if (c->dst_addr.ipv4.sin_addr.s_addr == con->dst_addr.ipv4.sin_addr.s_addr && - c->state > CON_STATE_REQUEST_END) { - conns_by_ip++; - - if (conns_by_ip > p->conf.max_conns) { + if (c->dst_addr.plain.sa_family != con->dst_addr.plain.sa_family) continue; + if (c->state <= CON_STATE_REQUEST_END) continue; + + switch (con->dst_addr.plain.sa_family) { + case AF_INET: + if (c->dst_addr.ipv4.sin_addr.s_addr != con->dst_addr.ipv4.sin_addr.s_addr) continue; + break; +#ifdef HAVE_IPV6 + case AF_INET6: + if (0 != memcmp(c->dst_addr.ipv6.sin6_addr.s6_addr, con->dst_addr.ipv6.sin6_addr.s6_addr, 16)) continue; + break; +#endif + default: /* Address family not supported, should never be reached */ + continue; + }; + conns_by_ip++; + + if (conns_by_ip > p->conf.max_conns) { + if (!p->conf.silent) { log_error_write(srv, __FILE__, __LINE__, "ss", inet_ntop_cache_get_ip(srv, &(con->dst_addr)), "turned away. Too many connections."); - - con->http_status = 403; - return HANDLER_FINISHED; } + + con->http_status = 403; + con->mode = DIRECT; + return HANDLER_FINISHED; } } - + return HANDLER_GO_ON; } +int mod_evasive_plugin_init(plugin *p); int mod_evasive_plugin_init(plugin *p) { p->version = LIGHTTPD_VERSION_ID; p->name = buffer_init_string("evasive"); - + p->init = mod_evasive_init; p->set_defaults = mod_evasive_set_defaults; p->handle_uri_clean = mod_evasive_uri_handler; p->cleanup = mod_evasive_free; - + p->data = NULL; - + return 0; } diff --git a/src/mod_evhost.c b/src/mod_evhost.c index bc8adb6..7aabf6e 100644 --- a/src/mod_evhost.c +++ b/src/mod_evhost.c @@ -1,16 +1,16 @@ -#include <string.h> -#include <errno.h> -#include <ctype.h> - #include "plugin.h" #include "log.h" #include "response.h" #include "stat_cache.h" +#include <string.h> +#include <errno.h> +#include <ctype.h> + typedef struct { /* unparsed pieces */ buffer *path_pieces_raw; - + /* pieces for path creation */ size_t len; buffer **path_pieces; @@ -21,14 +21,14 @@ typedef struct { buffer *tmp_buf; plugin_config **config_storage; - plugin_config conf; + plugin_config conf; } plugin_data; INIT_FUNC(mod_evhost_init) { plugin_data *p; - + p = calloc(1, sizeof(*p)); - + p->tmp_buf = buffer_init(); return p; @@ -36,34 +36,34 @@ INIT_FUNC(mod_evhost_init) { FREE_FUNC(mod_evhost_free) { plugin_data *p = p_d; - + UNUSED(srv); if (!p) return HANDLER_GO_ON; - + if (p->config_storage) { size_t i; for (i = 0; i < srv->config_context->used; i++) { plugin_config *s = p->config_storage[i]; if (!s) continue; - + if(s->path_pieces) { size_t j; for (j = 0; j < s->len; j++) { buffer_free(s->path_pieces[j]); } - + free(s->path_pieces); } - + buffer_free(s->path_pieces_raw); - + free(s); } free(p->config_storage); } - + buffer_free(p->tmp_buf); free(p); @@ -73,30 +73,30 @@ FREE_FUNC(mod_evhost_free) { static void mod_evhost_parse_pattern(plugin_config *s) { char *ptr = s->path_pieces_raw->ptr,*pos; - + s->path_pieces = NULL; - + for(pos=ptr;*ptr;ptr++) { if(*ptr == '%') { s->path_pieces = realloc(s->path_pieces,(s->len+2) * sizeof(*s->path_pieces)); s->path_pieces[s->len] = buffer_init(); s->path_pieces[s->len+1] = buffer_init(); - + buffer_copy_string_len(s->path_pieces[s->len],pos,ptr-pos); pos = ptr + 2; - + buffer_copy_string_len(s->path_pieces[s->len+1],ptr++,2); - + s->len += 2; } } - + if(*pos != '\0') { s->path_pieces = realloc(s->path_pieces,(s->len+1) * sizeof(*s->path_pieces)); s->path_pieces[s->len] = buffer_init(); - - buffer_append_memory(s->path_pieces[s->len],pos,ptr-pos); - + + buffer_copy_string_len(s->path_pieces[s->len],pos,ptr-pos); + s->len += 1; } } @@ -104,9 +104,9 @@ static void mod_evhost_parse_pattern(plugin_config *s) { SETDEFAULTS_FUNC(mod_evhost_set_defaults) { plugin_data *p = p_d; size_t i; - + /** - * + * * # * # define a pattern for the host url finding * # %% => % sign @@ -115,50 +115,52 @@ SETDEFAULTS_FUNC(mod_evhost_set_defaults) { * # %2 => domain name without tld * # %3 => subdomain 1 name * # %4 => subdomain 2 name + * # %_ => fqdn (without port info) * # * evhost.path-pattern = "/home/ckruse/dev/www/%3/htdocs/" - * + * */ - - config_values_t cv[] = { + + config_values_t cv[] = { { "evhost.path-pattern", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET } }; - + if (!p) return HANDLER_ERROR; - + p->config_storage = calloc(1, srv->config_context->used * sizeof(specific_config *)); - + for (i = 0; i < srv->config_context->used; i++) { plugin_config *s; - + s = calloc(1, sizeof(plugin_config)); s->path_pieces_raw = buffer_init(); s->path_pieces = NULL; s->len = 0; - + cv[0].destination = s->path_pieces_raw; - + p->config_storage[i] = s; - + if (0 != config_insert_values_global(srv, ((data_config *)srv->config_context->data[i])->value, cv)) { return HANDLER_ERROR; } - + if (s->path_pieces_raw->used != 0) { mod_evhost_parse_pattern(s); } } - + return HANDLER_GO_ON; } /** - * assign the different parts of the domain to array-indezes - * - %0 - full hostname (authority w/o port) + * assign the different parts of the domain to array-indezes (sub2.sub1.domain.tld) + * - %0 - domain.tld * - %1 - tld - * - %2 - domain.tld - * - %3 - + * - %2 - domain + * - %3 - sub1 + * - ... */ static int mod_evhost_parse_host(connection *con,array *host) { @@ -168,7 +170,7 @@ static int mod_evhost_parse_host(connection *con,array *host) { int first = 1; data_string *ds; int i; - + /* first, find the domain + tld */ for(;ptr > con->uri.authority->ptr;ptr--) { if(*ptr == '.') { @@ -179,45 +181,45 @@ static int mod_evhost_parse_host(connection *con,array *host) { first = 1; } } - + ds = data_string_init(); - buffer_copy_string(ds->key,"%0"); - + buffer_copy_string_len(ds->key,CONST_STR_LEN("%0")); + /* if we stopped at a dot, skip the dot */ if (*ptr == '.') ptr++; buffer_copy_string_len(ds->value, ptr, colon-ptr); - + array_insert_unique(host,(data_unset *)ds); - + /* if the : is not the start of the authority, go on parsing the hostname */ - + if (colon != con->uri.authority->ptr) { for(ptr = colon - 1, i = 1; ptr > con->uri.authority->ptr; ptr--) { if(*ptr == '.') { if (ptr != colon - 1) { /* is something between the dots */ ds = data_string_init(); - buffer_copy_string(ds->key,"%"); + buffer_copy_string_len(ds->key,CONST_STR_LEN("%")); buffer_append_long(ds->key, i++); buffer_copy_string_len(ds->value,ptr+1,colon-ptr-1); - + array_insert_unique(host,(data_unset *)ds); } colon = ptr; } } - + /* if the . is not the first charactor of the hostname */ if (colon != ptr) { ds = data_string_init(); - buffer_copy_string(ds->key,"%"); - buffer_append_long(ds->key, i++); + buffer_copy_string_len(ds->key,CONST_STR_LEN("%")); + buffer_append_long(ds->key, i /* ++ */); buffer_copy_string_len(ds->value,ptr,colon-ptr); - + array_insert_unique(host,(data_unset *)ds); } } - + return 0; } @@ -226,29 +228,29 @@ static int mod_evhost_parse_host(connection *con,array *host) { static int mod_evhost_patch_connection(server *srv, connection *con, plugin_data *p) { size_t i, j; plugin_config *s = p->config_storage[0]; - + PATCH(path_pieces); PATCH(len); - + /* skip the first, the global context */ for (i = 1; i < srv->config_context->used; i++) { data_config *dc = (data_config *)srv->config_context->data[i]; s = p->config_storage[i]; - + /* condition didn't match */ if (!config_check_cond(srv, con, dc)) continue; - + /* merge config */ for (j = 0; j < dc->value->used; j++) { data_unset *du = dc->value->data[j]; - + if (buffer_is_equal_string(du->key, CONST_STR_LEN("evhost.path-pattern"))) { PATCH(path_pieces); PATCH(len); } } } - + return 0; } #undef PATCH @@ -261,32 +263,42 @@ static handler_t mod_evhost_uri_handler(server *srv, connection *con, void *p_d) register char *ptr; int not_good = 0; stat_cache_entry *sce = NULL; - + /* not authority set */ if (con->uri.authority->used == 0) return HANDLER_GO_ON; - + mod_evhost_patch_connection(srv, con, p); - + /* missing even default(global) conf */ if (0 == p->conf.len) { return HANDLER_GO_ON; } parsed_host = array_init(); - + mod_evhost_parse_host(con, parsed_host); - + /* build document-root */ buffer_reset(p->tmp_buf); - + for (i = 0; i < p->conf.len; i++) { ptr = p->conf.path_pieces[i]->ptr; if (*ptr == '%') { data_string *ds; - + if (*(ptr+1) == '%') { /* %% */ - BUFFER_APPEND_STRING_CONST(p->tmp_buf,"%"); + buffer_append_string_len(p->tmp_buf,CONST_STR_LEN("%")); + } else if (*(ptr+1) == '_' ) { + /* %_ == full hostname */ + char *colon = strchr(con->uri.authority->ptr, ':'); + + if(colon == NULL) { + buffer_append_string_buffer(p->tmp_buf, con->uri.authority); /* adds fqdn */ + } else { + /* strip the port out of the authority-part of the URI scheme */ + buffer_append_string_len(p->tmp_buf, con->uri.authority->ptr, colon - con->uri.authority->ptr); /* adds fqdn */ + } } else if (NULL != (ds = (data_string *)array_get_element(parsed_host,p->conf.path_pieces[i]->ptr))) { if (ds->value->used) { buffer_append_string_buffer(p->tmp_buf,ds->value); @@ -298,11 +310,11 @@ static handler_t mod_evhost_uri_handler(server *srv, connection *con, void *p_d) buffer_append_string_buffer(p->tmp_buf,p->conf.path_pieces[i]); } } - + BUFFER_APPEND_SLASH(p->tmp_buf); - + array_free(parsed_host); - + if (HANDLER_ERROR == stat_cache_get_entry(srv, con, p->tmp_buf, &sce)) { log_error_write(srv, __FILE__, __LINE__, "sb", strerror(errno), p->tmp_buf); not_good = 1; @@ -310,14 +322,15 @@ static handler_t mod_evhost_uri_handler(server *srv, connection *con, void *p_d) log_error_write(srv, __FILE__, __LINE__, "sb", "not a directory:", p->tmp_buf); not_good = 1; } - + if (!not_good) { buffer_copy_string_buffer(con->physical.doc_root, p->tmp_buf); } - + return HANDLER_GO_ON; } +int mod_evhost_plugin_init(plugin *p); int mod_evhost_plugin_init(plugin *p) { p->version = LIGHTTPD_VERSION_ID; p->name = buffer_init_string("evhost"); @@ -325,9 +338,9 @@ int mod_evhost_plugin_init(plugin *p) { p->set_defaults = mod_evhost_set_defaults; p->handle_docroot = mod_evhost_uri_handler; p->cleanup = mod_evhost_free; - + p->data = NULL; - + return 0; } diff --git a/src/mod_expire.c b/src/mod_expire.c index 619b542..734cb93 100644 --- a/src/mod_expire.c +++ b/src/mod_expire.c @@ -1,8 +1,3 @@ -#include <ctype.h> -#include <stdlib.h> -#include <string.h> -#include <time.h> - #include "base.h" #include "log.h" #include "buffer.h" @@ -11,9 +6,14 @@ #include "plugin.h" #include "stat_cache.h" +#include <ctype.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> + /** - * this is a expire module for a lighttpd - * + * this is a expire module for a lighttpd + * * set 'Expires:' HTTP Headers on demand */ @@ -27,80 +27,83 @@ typedef struct { typedef struct { PLUGIN_DATA; - + buffer *expire_tstmp; - + plugin_config **config_storage; - - plugin_config conf; + + plugin_config conf; } plugin_data; /* init the plugin data */ INIT_FUNC(mod_expire_init) { plugin_data *p; - + p = calloc(1, sizeof(*p)); - + p->expire_tstmp = buffer_init(); - + buffer_prepare_copy(p->expire_tstmp, 255); - + return p; } /* detroy the plugin data */ FREE_FUNC(mod_expire_free) { plugin_data *p = p_d; - + UNUSED(srv); if (!p) return HANDLER_GO_ON; - + buffer_free(p->expire_tstmp); - + if (p->config_storage) { size_t i; for (i = 0; i < srv->config_context->used; i++) { plugin_config *s = p->config_storage[i]; - + if (!s) continue; + array_free(s->expire_url); - free(s); } free(p->config_storage); } - + free(p); - + return HANDLER_GO_ON; } -static int mod_expire_get_offset(server *srv, plugin_data *p, buffer *expire, int *offset) { +static int mod_expire_get_offset(server *srv, plugin_data *p, buffer *expire, time_t *offset) { char *ts; int type = -1; - int retts = 0; - + time_t retts = 0; + UNUSED(p); - /* + /* * parse - * - * '(access|modification) [plus] {<num> <type>}*' - * + * + * '(access|now|modification) [plus] {<num> <type>}*' + * * e.g. 'access 1 years' */ - + if (expire->used == 0) { - log_error_write(srv, __FILE__, __LINE__, "s", + log_error_write(srv, __FILE__, __LINE__, "s", "empty:"); return -1; } - + ts = expire->ptr; - + if (0 == strncmp(ts, "access ", 7)) { type = 0; ts += 7; + } else if (0 == strncmp(ts, "now ", 4)) { + type = 0; + ts += 4; } else if (0 == strncmp(ts, "modification ", 13)) { type = 1; ts += 13; @@ -110,44 +113,47 @@ static int mod_expire_get_offset(server *srv, plugin_data *p, buffer *expire, in "invalid <base>:", ts); return -1; } - + if (0 == strncmp(ts, "plus ", 5)) { /* skip the optional plus */ ts += 5; } - - /* the rest is just <number> (years|months|days|hours|minutes|seconds) */ + + /* the rest is just <number> (years|months|weeks|days|hours|minutes|seconds) */ while (1) { char *space, *err; int num; - + if (NULL == (space = strchr(ts, ' '))) { - log_error_write(srv, __FILE__, __LINE__, "ss", + log_error_write(srv, __FILE__, __LINE__, "ss", "missing space after <num>:", ts); return -1; } - + num = strtol(ts, &err, 10); if (*err != ' ') { - log_error_write(srv, __FILE__, __LINE__, "ss", + log_error_write(srv, __FILE__, __LINE__, "ss", "missing <type> after <num>:", ts); return -1; } - + ts = space + 1; - + if (NULL != (space = strchr(ts, ' '))) { int slen; /* */ - + slen = space - ts; - - if (slen == 5 && + + if (slen == 5 && 0 == strncmp(ts, "years", slen)) { num *= 60 * 60 * 24 * 30 * 12; } else if (slen == 6 && 0 == strncmp(ts, "months", slen)) { num *= 60 * 60 * 24 * 30; + } else if (slen == 5 && + 0 == strncmp(ts, "weeks", slen)) { + num *= 60 * 60 * 24 * 7; } else if (slen == 4 && 0 == strncmp(ts, "days", slen)) { num *= 60 * 60 * 24; @@ -161,19 +167,21 @@ static int mod_expire_get_offset(server *srv, plugin_data *p, buffer *expire, in 0 == strncmp(ts, "seconds", slen)) { num *= 1; } else { - log_error_write(srv, __FILE__, __LINE__, "ss", + log_error_write(srv, __FILE__, __LINE__, "ss", "unknown type:", ts); return -1; } - + retts += num; - + ts = space + 1; } else { if (0 == strcmp(ts, "years")) { num *= 60 * 60 * 24 * 30 * 12; } else if (0 == strcmp(ts, "months")) { num *= 60 * 60 * 24 * 30; + } else if (0 == strcmp(ts, "weeks")) { + num *= 60 * 60 * 24 * 7; } else if (0 == strcmp(ts, "days")) { num *= 60 * 60 * 24; } else if (0 == strcmp(ts, "hours")) { @@ -183,19 +191,19 @@ static int mod_expire_get_offset(server *srv, plugin_data *p, buffer *expire, in } else if (0 == strcmp(ts, "seconds")) { num *= 1; } else { - log_error_write(srv, __FILE__, __LINE__, "ss", + log_error_write(srv, __FILE__, __LINE__, "ss", "unknown type:", ts); return -1; } - + retts += num; - + break; } } - + if (offset != NULL) *offset = retts; - + return type; } @@ -205,43 +213,43 @@ static int mod_expire_get_offset(server *srv, plugin_data *p, buffer *expire, in SETDEFAULTS_FUNC(mod_expire_set_defaults) { plugin_data *p = p_d; size_t i = 0, k; - - config_values_t cv[] = { + + config_values_t cv[] = { { "expire.url", NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION }, /* 0 */ { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET } }; - + if (!p) return HANDLER_ERROR; - + p->config_storage = calloc(1, srv->config_context->used * sizeof(specific_config *)); - + for (i = 0; i < srv->config_context->used; i++) { plugin_config *s; - + s = calloc(1, sizeof(plugin_config)); s->expire_url = array_init(); - + cv[0].destination = s->expire_url; - + p->config_storage[i] = s; - + if (0 != config_insert_values_global(srv, ((data_config *)srv->config_context->data[i])->value, cv)) { return HANDLER_ERROR; } - + for (k = 0; k < s->expire_url->used; k++) { data_string *ds = (data_string *)s->expire_url->data[k]; - + /* parse lines */ if (-1 == mod_expire_get_offset(srv, p, ds->value, NULL)) { - log_error_write(srv, __FILE__, __LINE__, "sb", + log_error_write(srv, __FILE__, __LINE__, "sb", "parsing expire.url failed:", ds->value); return HANDLER_ERROR; } } } - - + + return HANDLER_GO_ON; } @@ -250,27 +258,27 @@ SETDEFAULTS_FUNC(mod_expire_set_defaults) { static int mod_expire_patch_connection(server *srv, connection *con, plugin_data *p) { size_t i, j; plugin_config *s = p->config_storage[0]; - + PATCH(expire_url); - + /* skip the first, the global context */ for (i = 1; i < srv->config_context->used; i++) { data_config *dc = (data_config *)srv->config_context->data[i]; s = p->config_storage[i]; - + /* condition didn't match */ if (!config_check_cond(srv, con, dc)) continue; - + /* merge config */ for (j = 0; j < dc->value->used; j++) { data_unset *du = dc->value->data[j]; - + if (buffer_is_equal_string(du->key, CONST_STR_LEN("expire.url"))) { PATCH(expire_url); } } } - + return 0; } #undef PATCH @@ -279,83 +287,84 @@ URIHANDLER_FUNC(mod_expire_path_handler) { plugin_data *p = p_d; int s_len; size_t k; - + if (con->uri.path->used == 0) return HANDLER_GO_ON; - + mod_expire_patch_connection(srv, con, p); - + s_len = con->uri.path->used - 1; - + for (k = 0; k < p->conf.expire_url->used; k++) { data_string *ds = (data_string *)p->conf.expire_url->data[k]; int ct_len = ds->key->used - 1; - + if (ct_len > s_len) continue; if (ds->key->used == 0) continue; - + if (0 == strncmp(con->uri.path->ptr, ds->key->ptr, ct_len)) { - int ts; - time_t t; + time_t ts, expires; size_t len; stat_cache_entry *sce = NULL; - + stat_cache_get_entry(srv, con, con->physical.path, &sce); - + switch(mod_expire_get_offset(srv, p, ds->value, &ts)) { case 0: /* access */ - t = (ts + srv->cur_ts); + expires = (ts + srv->cur_ts); break; case 1: /* modification */ - - t = (ts + sce->st.st_mtime); + + expires = (ts + sce->st.st_mtime); break; default: /* -1 is handled at parse-time */ break; } - - - if (0 == (len = strftime(p->expire_tstmp->ptr, p->expire_tstmp->size - 1, - "%a, %d %b %Y %H:%M:%S GMT", gmtime(&(t))))) { + + /* expires should be at least srv->cur_ts */ + if (expires < srv->cur_ts) expires = srv->cur_ts; + + if (0 == (len = strftime(p->expire_tstmp->ptr, p->expire_tstmp->size - 1, + "%a, %d %b %Y %H:%M:%S GMT", gmtime(&(expires))))) { /* could not set expire header, out of mem */ - + return HANDLER_GO_ON; - } - + p->expire_tstmp->used = len + 1; - - /* HTTP/1.0 */ + + /* HTTP/1.0 */ response_header_overwrite(srv, con, CONST_STR_LEN("Expires"), CONST_BUF_LEN(p->expire_tstmp)); - /* HTTP/1.1 */ - buffer_copy_string(p->expire_tstmp, "max-age="); - buffer_append_long(p->expire_tstmp, ts); - - response_header_overwrite(srv, con, CONST_STR_LEN("Cache-Control"), CONST_BUF_LEN(p->expire_tstmp)); - + /* HTTP/1.1 */ + buffer_copy_string_len(p->expire_tstmp, CONST_STR_LEN("max-age=")); + buffer_append_long(p->expire_tstmp, expires - srv->cur_ts); /* as expires >= srv->cur_ts the difference is >= 0 */ + + response_header_append(srv, con, CONST_STR_LEN("Cache-Control"), CONST_BUF_LEN(p->expire_tstmp)); + return HANDLER_GO_ON; } } - + /* not found */ return HANDLER_GO_ON; } /* this function is called at dlopen() time and inits the callbacks */ +int mod_expire_plugin_init(plugin *p); int mod_expire_plugin_init(plugin *p) { p->version = LIGHTTPD_VERSION_ID; p->name = buffer_init_string("expire"); - + p->init = mod_expire_init; p->handle_subrequest_start = mod_expire_path_handler; p->set_defaults = mod_expire_set_defaults; p->cleanup = mod_expire_free; - + p->data = NULL; - + return 0; } diff --git a/src/mod_extforward.c b/src/mod_extforward.c new file mode 100644 index 0000000..828bbfe --- /dev/null +++ b/src/mod_extforward.c @@ -0,0 +1,517 @@ +#include "base.h" +#include "log.h" +#include "buffer.h" + +#include "plugin.h" + +#include "inet_ntop_cache.h" +#include "configfile.h" + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <ctype.h> +#include <stdlib.h> +#include <string.h> +#include <stdio.h> +#include <netinet/in.h> +#include <errno.h> + +/** + * mod_extforward.c for lighttpd, by comman.kang <at> gmail <dot> com + * extended, modified by Lionel Elie Mamane (LEM), lionel <at> mamane <dot> lu + * support chained proxies by glen@delfi.ee, #1528 + * + * Config example: + * + * Trust proxy 10.0.0.232 and 10.0.0.232 + * extforward.forwarder = ( "10.0.0.232" => "trust", + * "10.0.0.233" => "trust" ) + * + * Trust all proxies (NOT RECOMMENDED!) + * extforward.forwarder = ( "all" => "trust") + * + * Note that "all" has precedence over specific entries, + * so "all except" setups will not work. + * + * In case you have chained proxies, you can add all their IP's to the + * config. However "all" has effect only on connecting IP, as the + * X-Forwarded-For header can not be trusted. + * + * Note: The effect of this module is variable on $HTTP["remotip"] directives and + * other module's remote ip dependent actions. + * Things done by modules before we change the remoteip or after we reset it will match on the proxy's IP. + * Things done in between these two moments will match on the real client's IP. + * The moment things are done by a module depends on in which hook it does things and within the same hook + * on whether they are before/after us in the module loading order + * (order in the server.modules directive in the config file). + * + * Tested behaviours: + * + * mod_access: Will match on the real client. + * + * mod_accesslog: + * In order to see the "real" ip address in access log , + * you'll have to load mod_extforward after mod_accesslog. + * like this: + * + * server.modules = ( + * ..... + * mod_accesslog, + * mod_extforward + * ) + * + * Known issues: + * seems causing segfault with mod_ssl and $HTTP{"socket"} directives + * LEM 2006.05.26: Fixed segfault $SERVER["socket"] directive. Untested with SSL. + * + * ChangeLog: + * 2005.12.19 Initial Version + * 2005.12.19 fixed conflict with conditional directives + * 2006.05.26 LEM: IPv6 support + * 2006.05.26 LEM: Fix a segfault with $SERVER["socket"] directive. + * 2006.05.26 LEM: Run at uri_raw time, as we don't need to see the URI + * In this manner, we run before mod_access and $HTTP["remoteip"] directives work! + * 2006.05.26 LEM: Clean config_cond cache of tests whose result we probably change. + */ + + +/* plugin config for all request/connections */ + +typedef struct { + array *forwarder; + array *headers; +} plugin_config; + +typedef struct { + PLUGIN_DATA; + + plugin_config **config_storage; + + plugin_config conf; +} plugin_data; + + +/* context , used for restore remote ip */ + +typedef struct { + sock_addr saved_remote_addr; + buffer *saved_remote_addr_buf; +} handler_ctx; + + +static handler_ctx * handler_ctx_init(sock_addr oldaddr, buffer *oldaddr_buf) { + handler_ctx * hctx; + hctx = calloc(1, sizeof(*hctx)); + hctx->saved_remote_addr = oldaddr; + hctx->saved_remote_addr_buf = oldaddr_buf; + return hctx; +} + +static void handler_ctx_free(handler_ctx *hctx) { + free(hctx); +} + +/* init the plugin data */ +INIT_FUNC(mod_extforward_init) { + plugin_data *p; + p = calloc(1, sizeof(*p)); + return p; +} + +/* destroy the plugin data */ +FREE_FUNC(mod_extforward_free) { + plugin_data *p = p_d; + + UNUSED(srv); + + if (!p) return HANDLER_GO_ON; + + if (p->config_storage) { + size_t i; + + for (i = 0; i < srv->config_context->used; i++) { + plugin_config *s = p->config_storage[i]; + + if (!s) continue; + + array_free(s->forwarder); + array_free(s->headers); + + free(s); + } + free(p->config_storage); + } + + + free(p); + + return HANDLER_GO_ON; +} + +/* handle plugin config and check values */ + +SETDEFAULTS_FUNC(mod_extforward_set_defaults) { + plugin_data *p = p_d; + size_t i = 0; + + config_values_t cv[] = { + { "extforward.forwarder", NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION }, /* 0 */ + { "extforward.headers", NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION }, /* 1 */ + { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET } + }; + + if (!p) return HANDLER_ERROR; + + p->config_storage = calloc(1, srv->config_context->used * sizeof(specific_config *)); + + for (i = 0; i < srv->config_context->used; i++) { + plugin_config *s; + + s = calloc(1, sizeof(plugin_config)); + s->forwarder = array_init(); + s->headers = array_init(); + + cv[0].destination = s->forwarder; + cv[1].destination = s->headers; + + p->config_storage[i] = s; + + if (0 != config_insert_values_global(srv, ((data_config *)srv->config_context->data[i])->value, cv)) { + return HANDLER_ERROR; + } + } + + return HANDLER_GO_ON; +} + +#define PATCH(x) \ + p->conf.x = s->x; +static int mod_extforward_patch_connection(server *srv, connection *con, plugin_data *p) { + size_t i, j; + plugin_config *s = p->config_storage[0]; + + PATCH(forwarder); + PATCH(headers); + + /* skip the first, the global context */ + for (i = 1; i < srv->config_context->used; i++) { + data_config *dc = (data_config *)srv->config_context->data[i]; + s = p->config_storage[i]; + + /* condition didn't match */ + if (!config_check_cond(srv, con, dc)) continue; + + /* merge config */ + for (j = 0; j < dc->value->used; j++) { + data_unset *du = dc->value->data[j]; + + if (buffer_is_equal_string(du->key, CONST_STR_LEN("extforward.forwarder"))) { + PATCH(forwarder); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("extforward.headers"))) { + PATCH(headers); + } + } + } + + return 0; +} +#undef PATCH + + +static void put_string_into_array_len(array *ary, const char *str, int len) +{ + data_string *tempdata; + if (len == 0) + return; + tempdata = data_string_init(); + buffer_copy_string_len(tempdata->value,str,len); + array_insert_unique(ary,(data_unset *)tempdata); +} +/* + extract a forward array from the environment +*/ +static array *extract_forward_array(buffer *pbuffer) +{ + array *result = array_init(); + if (pbuffer->used > 0) { + char *base, *curr; + /* state variable, 0 means not in string, 1 means in string */ + int in_str = 0; + for (base = pbuffer->ptr, curr = pbuffer->ptr; *curr; curr++) { + if (in_str) { + if ((*curr > '9' || *curr < '0') && *curr != '.' && *curr != ':' && (*curr < 'a' || *curr > 'f') && (*curr < 'A' || *curr > 'F')) { + /* found an separator , insert value into result array */ + put_string_into_array_len(result, base, curr - base); + /* change state to not in string */ + in_str = 0; + } + } else { + if ((*curr >= '0' && *curr <= '9') || *curr == ':' || (*curr >= 'a' && *curr <= 'f') || (*curr >= 'A' && *curr <= 'F')) { + /* found leading char of an IP address, move base pointer and change state */ + base = curr; + in_str = 1; + } + } + } + /* if breaking out while in str, we got to the end of string, so add it */ + if (in_str) { + put_string_into_array_len(result, base, curr - base); + } + } + return result; +} + +#define IP_TRUSTED 1 +#define IP_UNTRUSTED 0 +/* + * check whether ip is trusted, return 1 for trusted , 0 for untrusted + */ +static int is_proxy_trusted(const char *ipstr, plugin_data *p) +{ + data_string* allds = (data_string *)array_get_element(p->conf.forwarder, "all"); + + if (allds) { + if (strcasecmp(allds->value->ptr, "trust") == 0) { + return IP_TRUSTED; + } else { + return IP_UNTRUSTED; + } + } + + return (data_string *)array_get_element(p->conf.forwarder, ipstr) ? IP_TRUSTED : IP_UNTRUSTED; +} + +/* + * Return char *ip of last address of proxy that is not trusted. + * Do not accept "all" keyword here. + */ +static const char *last_not_in_array(array *a, plugin_data *p) +{ + array *forwarder = p->conf.forwarder; + int i; + + for (i = a->used - 1; i >= 0; i--) { + data_string *ds = (data_string *)a->data[i]; + const char *ip = ds->value->ptr; + + if (!array_get_element(forwarder, ip)) { + return ip; + } + } + return NULL; +} + +static struct addrinfo *ipstr_to_sockaddr(server *srv, const char *host) { + struct addrinfo hints, *res0; + int result; + + memset(&hints, 0, sizeof(hints)); +#ifndef AI_NUMERICSERV + /** + * quoting $ man getaddrinfo + * + * NOTES + * AI_ADDRCONFIG, AI_ALL, and AI_V4MAPPED are available since glibc 2.3.3. + * AI_NUMERICSERV is available since glibc 2.3.4. + */ +#define AI_NUMERICSERV 0 +#endif + hints.ai_flags = AI_NUMERICHOST | AI_NUMERICSERV; + + errno = 0; + result = getaddrinfo(host, NULL, &hints, &res0); + + if (result != 0) { + log_error_write(srv, __FILE__, __LINE__, "SSSs(S)", + "could not resolve hostname ", host, " because ", gai_strerror(result), strerror(errno)); + + return NULL; + } else if (res0 == NULL) { + log_error_write(srv, __FILE__, __LINE__, "SSS", + "Problem in resolving hostname ", host, ": succeeded, but no information returned"); + } + + return res0; +} + + + +static void clean_cond_cache(server *srv, connection *con) { + config_cond_cache_reset_item(srv, con, COMP_HTTP_REMOTE_IP); +} + +URIHANDLER_FUNC(mod_extforward_uri_handler) { + plugin_data *p = p_d; + data_string *forwarded = NULL; +#ifdef HAVE_IPV6 + char b2[INET6_ADDRSTRLEN + 1]; + struct addrinfo *addrlist = NULL; +#endif + const char *dst_addr_str = NULL; + array *forward_array = NULL; + const char *real_remote_addr = NULL; +#ifdef HAVE_IPV6 +#endif + + if (!con->request.headers) return HANDLER_GO_ON; + + mod_extforward_patch_connection(srv, con, p); + + if (con->conf.log_request_handling) { + log_error_write(srv, __FILE__, __LINE__, "s", + "-- mod_extforward_uri_handler called"); + } + + if (p->conf.headers->used) { + data_string *ds; + size_t k; + + for(k = 0; k < p->conf.headers->used; k++) { + ds = (data_string *) p->conf.headers->data[k]; + if (NULL != (forwarded = (data_string*) array_get_element(con->request.headers, ds->value->ptr))) break; + } + } else { + forwarded = (data_string *) array_get_element(con->request.headers,"X-Forwarded-For"); + if (NULL == forwarded) forwarded = (data_string *) array_get_element(con->request.headers, "Forwarded-For"); + } + + if (NULL == forwarded) { + if (con->conf.log_request_handling) { + log_error_write(srv, __FILE__, __LINE__, "s", "no forward header found, skipping"); + } + + return HANDLER_GO_ON; + } + +#ifdef HAVE_IPV6 + dst_addr_str = inet_ntop(con->dst_addr.plain.sa_family, + con->dst_addr.plain.sa_family == AF_INET6 ? + (struct sockaddr *)&(con->dst_addr.ipv6.sin6_addr) : + (struct sockaddr *)&(con->dst_addr.ipv4.sin_addr), + b2, (sizeof b2) - 1); +#else + dst_addr_str = inet_ntoa(con->dst_addr.ipv4.sin_addr); +#endif + + /* if the remote ip itself is not trusted, then do nothing */ + if (IP_UNTRUSTED == is_proxy_trusted(dst_addr_str, p)) { + if (con->conf.log_request_handling) { + log_error_write(srv, __FILE__, __LINE__, "sss", + "remote address", dst_addr_str, "is NOT a trusted proxy, skipping"); + } + + return HANDLER_GO_ON; + } + + /* build forward_array from forwarded data_string */ + forward_array = extract_forward_array(forwarded->value); + real_remote_addr = last_not_in_array(forward_array, p); + + if (real_remote_addr != NULL) { /* parsed */ + sock_addr sock; + struct addrinfo *addrs_left; + server_socket *srv_sock = con->srv_socket; + data_string *forwarded_proto = (data_string *)array_get_element(con->request.headers, "X-Forwarded-Proto"); + + if (forwarded_proto && !strcmp(forwarded_proto->value->ptr, "https")) { + srv_sock->is_proxy_ssl = 1; + } else { + srv_sock->is_proxy_ssl = 0; + } + + if (con->conf.log_request_handling) { + log_error_write(srv, __FILE__, __LINE__, "ss", "using address:", real_remote_addr); + } +#ifdef HAVE_IPV6 + addrlist = ipstr_to_sockaddr(srv, real_remote_addr); + sock.plain.sa_family = AF_UNSPEC; + for (addrs_left = addrlist; addrs_left != NULL; addrs_left = addrs_left -> ai_next) { + sock.plain.sa_family = addrs_left->ai_family; + if (sock.plain.sa_family == AF_INET) { + sock.ipv4.sin_addr = ((struct sockaddr_in*)addrs_left->ai_addr)->sin_addr; + break; + } else if (sock.plain.sa_family == AF_INET6) { + sock.ipv6.sin6_addr = ((struct sockaddr_in6*)addrs_left->ai_addr)->sin6_addr; + break; + } + } +#else + UNUSED(addrs_left); + sock.ipv4.sin_addr.s_addr = inet_addr(real_remote_addr); + sock.plain.sa_family = (sock.ipv4.sin_addr.s_addr == 0xFFFFFFFF) ? AF_UNSPEC : AF_INET; +#endif + if (sock.plain.sa_family != AF_UNSPEC) { + /* we found the remote address, modify current connection and save the old address */ + if (con->plugin_ctx[p->id]) { + log_error_write(srv, __FILE__, __LINE__, "s", + "patching an already patched connection!"); + handler_ctx_free(con->plugin_ctx[p->id]); + con->plugin_ctx[p->id] = NULL; + } + /* save old address */ + con->plugin_ctx[p->id] = handler_ctx_init(con->dst_addr, con->dst_addr_buf); + /* patch connection address */ + con->dst_addr = sock; + con->dst_addr_buf = buffer_init(); + buffer_copy_string(con->dst_addr_buf, real_remote_addr); + + if (con->conf.log_request_handling) { + log_error_write(srv, __FILE__, __LINE__, "ss", + "patching con->dst_addr_buf for the accesslog:", real_remote_addr); + } + /* Now, clean the conf_cond cache, because we may have changed the results of tests */ + clean_cond_cache(srv, con); + } +#ifdef HAVE_IPV6 + if (addrlist != NULL ) freeaddrinfo(addrlist); +#endif + } + array_free(forward_array); + + /* not found */ + return HANDLER_GO_ON; +} + +CONNECTION_FUNC(mod_extforward_restore) { + plugin_data *p = p_d; + handler_ctx *hctx = con->plugin_ctx[p->id]; + + if (!hctx) return HANDLER_GO_ON; + + con->dst_addr = hctx->saved_remote_addr; + buffer_free(con->dst_addr_buf); + + con->dst_addr_buf = hctx->saved_remote_addr_buf; + + handler_ctx_free(hctx); + + con->plugin_ctx[p->id] = NULL; + + /* Now, clean the conf_cond cache, because we may have changed the results of tests */ + clean_cond_cache(srv, con); + + return HANDLER_GO_ON; +} + + +/* this function is called at dlopen() time and inits the callbacks */ + +int mod_extforward_plugin_init(plugin *p); +int mod_extforward_plugin_init(plugin *p) { + p->version = LIGHTTPD_VERSION_ID; + p->name = buffer_init_string("extforward"); + + p->init = mod_extforward_init; + p->handle_uri_raw = mod_extforward_uri_handler; + p->handle_request_done = mod_extforward_restore; + p->connection_reset = mod_extforward_restore; + p->set_defaults = mod_extforward_set_defaults; + p->cleanup = mod_extforward_free; + + p->data = NULL; + + return 0; +} + diff --git a/src/mod_fastcgi.c b/src/mod_fastcgi.c index 8b1be0a..de6bfd1 100644 --- a/src/mod_fastcgi.c +++ b/src/mod_fastcgi.c @@ -1,13 +1,3 @@ -#include <sys/types.h> -#include <unistd.h> -#include <errno.h> -#include <fcntl.h> -#include <string.h> -#include <stdlib.h> -#include <ctype.h> -#include <assert.h> -#include <signal.h> - #include "buffer.h" #include "server.h" #include "keyvalue.h" @@ -23,8 +13,28 @@ #include "inet_ntop_cache.h" #include "stat_cache.h" +#include "status_counter.h" + +#include <sys/types.h> +#include <unistd.h> +#include <errno.h> +#include <fcntl.h> +#include <string.h> +#include <stdlib.h> +#include <ctype.h> +#include <assert.h> +#include <signal.h> + +#ifdef HAVE_FASTCGI_FASTCGI_H +# include <fastcgi/fastcgi.h> +#else +# ifdef HAVE_FASTCGI_H +# include <fastcgi.h> +# else +# include "fastcgi.h" +# endif +#endif /* HAVE_FASTCGI_FASTCGI_H */ -#include <fastcgi.h> #include <stdio.h> #ifdef HAVE_SYS_FILIO_H @@ -33,11 +43,6 @@ #include "sys-socket.h" - -#ifndef UNIX_PATH_MAX -# define UNIX_PATH_MAX 108 -#endif - #ifdef HAVE_SYS_UIO_H #include <sys/uio.h> #endif @@ -45,42 +50,52 @@ #include <sys/wait.h> #endif +#include "version.h" + +#define FCGI_ENV_ADD_CHECK(ret, con) \ + if (ret == -1) { \ + con->http_status = 400; \ + con->file_finished = 1; \ + return -1; \ + }; /* - * + * * TODO: - * + * * - add timeout for a connect to a non-fastcgi process * (use state_timestamp + state) - * + * */ typedef struct fcgi_proc { size_t id; /* id will be between 1 and max_procs */ - buffer *socket; /* config.socket + "-" + id */ + buffer *unixsocket; /* config.socket + "-" + id */ unsigned port; /* config.port + pno */ - + + buffer *connection_name; /* either tcp:<host>:<port> or unix:<socket> for debugging purposes */ + pid_t pid; /* PID of the spawned process (0 if not spawned locally) */ size_t load; /* number of requests waiting on this process */ - time_t last_used; /* see idle_timeout */ size_t requests; /* see max_requests */ struct fcgi_proc *prev, *next; /* see first */ - - time_t disabled_until; /* this proc is disabled until, use something else until than */ - + + time_t disabled_until; /* this proc is disabled until, use something else until then */ + int is_local; - enum { - PROC_STATE_UNSET, /* init-phase */ - PROC_STATE_RUNNING, /* alive */ - PROC_STATE_DIED_WAIT_FOR_PID, - PROC_STATE_KILLED, /* was killed as we don't have the load anymore */ - PROC_STATE_DIED, /* marked as dead, should be restarted */ - PROC_STATE_DISABLED /* proc disabled as it resulted in an error */ - } state; + enum { + PROC_STATE_UNSET, /* init-phase */ + PROC_STATE_RUNNING, /* alive */ + PROC_STATE_OVERLOADED, /* listen-queue is full, + don't send anything to this proc for the next 2 seconds */ + PROC_STATE_DIED_WAIT_FOR_PID, /* */ + PROC_STATE_DIED, /* marked as dead, should be restarted */ + PROC_STATE_KILLED /* was killed as we don't have the load anymore */ + } state; } fcgi_proc; typedef struct { @@ -91,69 +106,56 @@ typedef struct { * sorted by lowest load * * whenever a job is done move it up in the list - * until it is sorted, move it down as soon as the + * until it is sorted, move it down as soon as the * job is started */ - fcgi_proc *first; - fcgi_proc *unused_procs; + fcgi_proc *first; + fcgi_proc *unused_procs; - /* + /* * spawn at least min_procs, at max_procs. * - * as soon as the load of the first entry + * as soon as the load of the first entry * is max_load_per_proc we spawn a new one - * and add it to the first entry and give it + * and add it to the first entry and give it * the load - * + * */ - unsigned short min_procs; unsigned short max_procs; size_t num_procs; /* how many procs are started */ - size_t active_procs; /* how many of them are really running */ + size_t active_procs; /* how many of them are really running, i.e. state = PROC_STATE_RUNNING */ - unsigned short max_load_per_proc; - - /* - * kick the process from the list if it was not - * used for idle_timeout until min_procs is - * reached. this helps to get the processlist - * small again we had a small peak load. - * - */ - - unsigned short idle_timeout; - /* * time after a disabled remote connection is tried to be re-enabled - * - * + * + * */ - + unsigned short disable_time; /* - * same fastcgi processes get a little bit larger - * than wanted. max_requests_per_proc kills a + * some fastcgi processes get a little bit larger + * than wanted. max_requests_per_proc kills a * process after a number of handled requests. * */ size_t max_requests_per_proc; - + /* config */ - /* - * host:port + /* + * host:port * - * if host is one of the local IP adresses the + * if host is one of the local IP adresses the * whole connection is local * - * if tcp/ip should be used host AND port have - * to be specified - * - */ - buffer *host; + * if port is not 0, and host is not specified, + * "localhost" (INADDR_LOOPBACK) is assumed. + * + */ + buffer *host; unsigned short port; /* @@ -166,27 +168,27 @@ typedef struct { */ buffer *unixsocket; - /* if socket is local we can start the fastcgi + /* if socket is local we can start the fastcgi * process ourself * * bin-path is the path to the binary * * check min_procs and max_procs for the number - * of process to start-up + * of process to start up */ - buffer *bin_path; - - /* bin-path is set bin-environment is taken to + buffer *bin_path; + + /* bin-path is set bin-environment is taken to * create the environement before starting the * FastCGI process - * + * */ array *bin_env; - + array *bin_env_copy; - + /* - * docroot-translation between URL->phys and the + * docroot-translation between URL->phys and the * remote host * * reasons: @@ -205,7 +207,7 @@ typedef struct { unsigned short mode; /* - * check_local tell you if the phys file is stat()ed + * check_local tells you if the phys file is stat()ed * or not. FastCGI doesn't care if the service is * remote. If the web-server side doesn't contain * the fastcgi-files we should not stat() for them @@ -215,44 +217,59 @@ typedef struct { /* * append PATH_INFO to SCRIPT_FILENAME - * - * php needs this if cgi.fix_pathinfo is provied - * + * + * php needs this if cgi.fix_pathinfo is provided + * */ - + unsigned short break_scriptfilename_for_php; /* + * workaround for program when prefix="/" + * + * rule to build PATH_INFO is hardcoded for when check_local is disabled + * enable this option to use the workaround + * + */ + + unsigned short fix_root_path_name; + + /* * If the backend includes X-LIGHTTPD-send-file in the response * we use the value as filename and ignore the content. * */ unsigned short allow_xsendfile; - + ssize_t load; /* replace by host->load */ size_t max_id; /* corresponds most of the time to num_procs. - + only if a process is killed max_id waits for the process itself - to die and decrements its afterwards */ + to die and decrements it afterwards */ buffer *strip_request_uri; + + unsigned short kill_signal; /* we need a setting for this as libfcgi + applications prefer SIGUSR1 while the + rest of the world would use SIGTERM + *sigh* */ } fcgi_extension_host; /* * one extension can have multiple hosts assigned - * one host can spawn additional processes on the same + * one host can spawn additional processes on the same * socket (if we control it) * * ext -> host -> procs * 1:n 1:n * - * if the fastcgi process is remote that whole goes down + * if the fastcgi process is remote that whole goes down * to * * ext -> host -> procs - * 1:n 1:1 + * 1:n 1:1 * * in case of PHP and FCGI_CHILDREN we have again a procs * but we don't control it directly. @@ -262,8 +279,11 @@ typedef struct { typedef struct { buffer *key; /* like .php */ + int note_is_sent; + int last_used_ndx; + fcgi_extension_host **hosts; - + size_t used; size_t size; } fcgi_extension; @@ -277,20 +297,16 @@ typedef struct { typedef struct { - fcgi_exts *exts; - - int debug; -} plugin_config; + fcgi_exts *exts; -typedef struct { - size_t *ptr; - size_t used; - size_t size; -} buffer_uint; + array *ext_mapping; + + unsigned int debug; +} plugin_config; typedef struct { char **ptr; - + size_t size; size_t used; } char_array; @@ -298,45 +314,44 @@ typedef struct { /* generic plugin data, shared between all connections */ typedef struct { PLUGIN_DATA; - buffer_uint fcgi_request_id; - + buffer *fcgi_env; - + buffer *path; buffer *parse_response; buffer *statuskey; - + plugin_config **config_storage; - + plugin_config conf; /* this is only used as long as no handler_ctx is setup */ } plugin_data; /* connection specific data */ -typedef enum { +typedef enum { FCGI_STATE_UNSET, - FCGI_STATE_INIT, - FCGI_STATE_CONNECT_DELAYED, - FCGI_STATE_PREPARE_WRITE, - FCGI_STATE_WRITE, - FCGI_STATE_READ + FCGI_STATE_INIT, + FCGI_STATE_CONNECT_DELAYED, + FCGI_STATE_PREPARE_WRITE, + FCGI_STATE_WRITE, + FCGI_STATE_READ } fcgi_connection_state_t; typedef struct { fcgi_proc *proc; fcgi_extension_host *host; fcgi_extension *ext; - + fcgi_connection_state_t state; time_t state_timestamp; - + int reconnects; /* number of reconnect attempts */ - + chunkqueue *rb; /* read queue */ chunkqueue *wb; /* write queue */ - + buffer *response_header; - + size_t request_id; int fd; /* fd to the fastcgi process */ int fde_ndx; /* index into the fd-event buffer */ @@ -345,75 +360,107 @@ typedef struct { int got_proc; int send_content_body; - + plugin_config conf; - + connection *remote_conn; /* dumb pointer */ plugin_data *plugin_data; /* dumb pointer */ } handler_ctx; /* ok, we need a prototype */ -static handler_t fcgi_handle_fdevent(void *s, void *ctx, int revents); +static handler_t fcgi_handle_fdevent(server *srv, void *ctx, int revents); + +static void reset_signals(void) { +#ifdef SIGTTOU + signal(SIGTTOU, SIG_DFL); +#endif +#ifdef SIGTTIN + signal(SIGTTIN, SIG_DFL); +#endif +#ifdef SIGTSTP + signal(SIGTSTP, SIG_DFL); +#endif + signal(SIGHUP, SIG_DFL); + signal(SIGPIPE, SIG_DFL); + signal(SIGUSR1, SIG_DFL); +} -int fcgi_proclist_sort_down(server *srv, fcgi_extension_host *host, fcgi_proc *proc); +static void fastcgi_status_copy_procname(buffer *b, fcgi_extension_host *host, fcgi_proc *proc) { + buffer_copy_string_len(b, CONST_STR_LEN("fastcgi.backend.")); + buffer_append_string_buffer(b, host->id); + if (proc) { + buffer_append_string_len(b, CONST_STR_LEN(".")); + buffer_append_long(b, proc->id); + } +} -data_integer *status_counter_get_counter(server *srv, const char *s, size_t len) { - data_integer *di; +static void fcgi_proc_load_inc(server *srv, handler_ctx *hctx) { + plugin_data *p = hctx->plugin_data; + hctx->proc->load++; - if (NULL == (di = (data_integer *)array_get_element(srv->status, s))) { - /* not found, create it */ + status_counter_inc(srv, CONST_STR_LEN("fastcgi.active-requests")); - if (NULL == (di = (data_integer *)array_get_unused_element(srv->status, TYPE_INTEGER))) { - di = data_integer_init(); - } - buffer_copy_string_len(di->key, s, len); - di->value = 0; + fastcgi_status_copy_procname(p->statuskey, hctx->host, hctx->proc); + buffer_append_string_len(p->statuskey, CONST_STR_LEN(".load")); - array_insert_unique(srv->status, (data_unset *)di); - } - return di; + status_counter_set(srv, CONST_BUF_LEN(p->statuskey), hctx->proc->load); } -/* dummies of the statistic framework functions - * they will be moved to a statistics.c later */ -int status_counter_inc(server *srv, const char *s, size_t len) { - data_integer *di = status_counter_get_counter(srv, s, len); +static void fcgi_proc_load_dec(server *srv, handler_ctx *hctx) { + plugin_data *p = hctx->plugin_data; + hctx->proc->load--; - di->value++; + status_counter_dec(srv, CONST_STR_LEN("fastcgi.active-requests")); - return 0; + fastcgi_status_copy_procname(p->statuskey, hctx->host, hctx->proc); + buffer_append_string_len(p->statuskey, CONST_STR_LEN(".load")); + + status_counter_set(srv, CONST_BUF_LEN(p->statuskey), hctx->proc->load); } -int status_counter_dec(server *srv, const char *s, size_t len) { - data_integer *di = status_counter_get_counter(srv, s, len); +static void fcgi_host_assign(server *srv, handler_ctx *hctx, fcgi_extension_host *host) { + plugin_data *p = hctx->plugin_data; + hctx->host = host; + hctx->host->load++; - if (di->value > 0) di->value--; + fastcgi_status_copy_procname(p->statuskey, hctx->host, NULL); + buffer_append_string_len(p->statuskey, CONST_STR_LEN(".load")); - return 0; + status_counter_set(srv, CONST_BUF_LEN(p->statuskey), hctx->host->load); } -int status_counter_set(server *srv, const char *s, size_t len, int val) { - data_integer *di = status_counter_get_counter(srv, s, len); +static void fcgi_host_reset(server *srv, handler_ctx *hctx) { + plugin_data *p = hctx->plugin_data; + hctx->host->load--; - di->value = val; + fastcgi_status_copy_procname(p->statuskey, hctx->host, NULL); + buffer_append_string_len(p->statuskey, CONST_STR_LEN(".load")); - return 0; + status_counter_set(srv, CONST_BUF_LEN(p->statuskey), hctx->host->load); + + hctx->host = NULL; } -int fastcgi_status_copy_procname(buffer *b, fcgi_extension_host *host, fcgi_proc *proc) { - buffer_copy_string(b, "fastcgi.backend."); - buffer_append_string_buffer(b, host->id); - buffer_append_string(b, "."); - buffer_append_long(b, proc->id); +static void fcgi_host_disable(server *srv, handler_ctx *hctx) { + plugin_data *p = hctx->plugin_data; - return 0; + if (hctx->host->disable_time || hctx->proc->is_local) { + if (hctx->proc->state == PROC_STATE_RUNNING) hctx->host->active_procs--; + hctx->proc->disabled_until = srv->cur_ts + hctx->host->disable_time; + hctx->proc->state = hctx->proc->is_local ? PROC_STATE_DIED_WAIT_FOR_PID : PROC_STATE_DIED; + + if (p->conf.debug) { + log_error_write(srv, __FILE__, __LINE__, "sds", + "backend disabled for", hctx->host->disable_time, "seconds"); + } + } } -int fastcgi_status_init(server *srv, buffer *b, fcgi_extension_host *host, fcgi_proc *proc) { +static int fastcgi_status_init(server *srv, buffer *b, fcgi_extension_host *host, fcgi_proc *proc) { #define CLEAN(x) \ fastcgi_status_copy_procname(b, host, proc); \ - buffer_append_string(b, x); \ + buffer_append_string_len(b, CONST_STR_LEN(x)); \ status_counter_set(srv, CONST_BUF_LEN(b), 0); CLEAN(".disabled"); @@ -422,37 +469,50 @@ int fastcgi_status_init(server *srv, buffer *b, fcgi_extension_host *host, fcgi_ CLEAN(".connected"); CLEAN(".load"); -#undef CLEAN +#undef CLEAN + +#define CLEAN(x) \ + fastcgi_status_copy_procname(b, host, NULL); \ + buffer_append_string_len(b, CONST_STR_LEN(x)); \ + status_counter_set(srv, CONST_BUF_LEN(b), 0); + + CLEAN(".load"); + +#undef CLEAN return 0; } -static handler_ctx * handler_ctx_init() { +static handler_ctx * handler_ctx_init(void) { handler_ctx * hctx; - + hctx = calloc(1, sizeof(*hctx)); assert(hctx); - + hctx->fde_ndx = -1; - + hctx->response_header = buffer_init(); - + hctx->request_id = 0; hctx->state = FCGI_STATE_INIT; hctx->proc = NULL; - + hctx->fd = -1; - + hctx->reconnects = 0; hctx->send_content_body = 1; hctx->rb = chunkqueue_init(); hctx->wb = chunkqueue_init(); - + return hctx; } -static void handler_ctx_free(handler_ctx *hctx) { +static void handler_ctx_free(server *srv, handler_ctx *hctx) { + if (hctx->host) { + fcgi_host_reset(srv, hctx); + } + buffer_free(hctx->response_header); chunkqueue_free(hctx->rb); @@ -461,29 +521,31 @@ static void handler_ctx_free(handler_ctx *hctx) { free(hctx); } -fcgi_proc *fastcgi_process_init() { +static fcgi_proc *fastcgi_process_init(void) { fcgi_proc *f; f = calloc(1, sizeof(*f)); - f->socket = buffer_init(); - + f->unixsocket = buffer_init(); + f->connection_name = buffer_init(); + f->prev = NULL; f->next = NULL; - + return f; } -void fastcgi_process_free(fcgi_proc *f) { +static void fastcgi_process_free(fcgi_proc *f) { if (!f) return; - + fastcgi_process_free(f->next); - - buffer_free(f->socket); - + + buffer_free(f->unixsocket); + buffer_free(f->connection_name); + free(f); } -fcgi_extension_host *fastcgi_host_init() { +static fcgi_extension_host *fastcgi_host_init(void) { fcgi_extension_host *f; f = calloc(1, sizeof(*f)); @@ -496,13 +558,13 @@ fcgi_extension_host *fastcgi_host_init() { f->bin_env = array_init(); f->bin_env_copy = array_init(); f->strip_request_uri = buffer_init(); - + return f; } -void fastcgi_host_free(fcgi_extension_host *h) { +static void fastcgi_host_free(fcgi_extension_host *h) { if (!h) return; - + buffer_free(h->id); buffer_free(h->host); buffer_free(h->unixsocket); @@ -511,53 +573,53 @@ void fastcgi_host_free(fcgi_extension_host *h) { buffer_free(h->strip_request_uri); array_free(h->bin_env); array_free(h->bin_env_copy); - + fastcgi_process_free(h->first); fastcgi_process_free(h->unused_procs); - + free(h); - + } -fcgi_exts *fastcgi_extensions_init() { +static fcgi_exts *fastcgi_extensions_init(void) { fcgi_exts *f; f = calloc(1, sizeof(*f)); - + return f; } -void fastcgi_extensions_free(fcgi_exts *f) { +static void fastcgi_extensions_free(fcgi_exts *f) { size_t i; - + if (!f) return; - + for (i = 0; i < f->used; i++) { fcgi_extension *fe; size_t j; - + fe = f->exts[i]; - + for (j = 0; j < fe->used; j++) { fcgi_extension_host *h; - + h = fe->hosts[j]; - + fastcgi_host_free(h); } - + buffer_free(fe->key); free(fe->hosts); - + free(fe); } - + free(f->exts); - + free(f); } -int fastcgi_extension_insert(fcgi_exts *ext, buffer *key, fcgi_extension_host *fh) { +static int fastcgi_extension_insert(fcgi_exts *ext, buffer *key, fcgi_extension_host *fh) { fcgi_extension *fe; size_t i; @@ -574,6 +636,7 @@ int fastcgi_extension_insert(fcgi_exts *ext, buffer *key, fcgi_extension_host *f fe = calloc(1, sizeof(*fe)); assert(fe); fe->key = buffer_init(); + fe->last_used_ndx = -1; buffer_copy_string_buffer(fe->key, key); /* */ @@ -602,105 +665,116 @@ int fastcgi_extension_insert(fcgi_exts *ext, buffer *key, fcgi_extension_host *f assert(fe->hosts); } - fe->hosts[fe->used++] = fh; + fe->hosts[fe->used++] = fh; return 0; - + } INIT_FUNC(mod_fastcgi_init) { plugin_data *p; - + p = calloc(1, sizeof(*p)); - + p->fcgi_env = buffer_init(); - + p->path = buffer_init(); p->parse_response = buffer_init(); p->statuskey = buffer_init(); - + return p; } FREE_FUNC(mod_fastcgi_free) { plugin_data *p = p_d; - buffer_uint *r = &(p->fcgi_request_id); - + UNUSED(srv); - if (r->ptr) free(r->ptr); - buffer_free(p->fcgi_env); buffer_free(p->path); buffer_free(p->parse_response); buffer_free(p->statuskey); - + if (p->config_storage) { size_t i, j, n; for (i = 0; i < srv->config_context->used; i++) { plugin_config *s = p->config_storage[i]; fcgi_exts *exts; - + if (!s) continue; - + exts = s->exts; for (j = 0; j < exts->used; j++) { fcgi_extension *ex; - + ex = exts->exts[j]; - + for (n = 0; n < ex->used; n++) { fcgi_proc *proc; fcgi_extension_host *host; - + host = ex->hosts[n]; - + for (proc = host->first; proc; proc = proc->next) { - if (proc->pid != 0) kill(proc->pid, SIGTERM); - - if (proc->is_local && - !buffer_is_empty(proc->socket)) { - unlink(proc->socket->ptr); + if (proc->pid != 0) { + kill(proc->pid, host->kill_signal); + } + + if (proc->is_local && + !buffer_is_empty(proc->unixsocket)) { + unlink(proc->unixsocket->ptr); } } - + for (proc = host->unused_procs; proc; proc = proc->next) { - if (proc->pid != 0) kill(proc->pid, SIGTERM); - - if (proc->is_local && - !buffer_is_empty(proc->socket)) { - unlink(proc->socket->ptr); + if (proc->pid != 0) { + kill(proc->pid, host->kill_signal); + } + if (proc->is_local && + !buffer_is_empty(proc->unixsocket)) { + unlink(proc->unixsocket->ptr); } } } } - + fastcgi_extensions_free(s->exts); - + array_free(s->ext_mapping); + free(s); } free(p->config_storage); } - + free(p); - + return HANDLER_GO_ON; } static int env_add(char_array *env, const char *key, size_t key_len, const char *val, size_t val_len) { char *dst; - + size_t i; + if (!key || !val) return -1; - + dst = malloc(key_len + val_len + 3); memcpy(dst, key, key_len); dst[key_len] = '='; - /* add the \0 from the value */ - memcpy(dst + key_len + 1, val, val_len + 1); - + memcpy(dst + key_len + 1, val, val_len); + dst[key_len + 1 + val_len] = '\0'; + + for (i = 0; i < env->used; i++) { + if (0 == strncmp(dst, env->ptr[i], key_len + 1)) { + /* don't care about free as we are in a forked child which is going to exec(...) */ + /* free(env->ptr[i]); */ + env->ptr[i] = dst; + return 0; + } + } + if (env->size == 0) { env->size = 16; env->ptr = malloc(env->size * sizeof(*env->ptr)); @@ -708,9 +782,9 @@ static int env_add(char_array *env, const char *key, size_t key_len, const char env->size += 16; env->ptr = realloc(env->ptr, env->size * sizeof(*env->ptr)); } - + env->ptr[env->used++] = dst; - + return 0; } @@ -729,15 +803,15 @@ static int parse_binpath(char_array *env, buffer *b) { if (env->size == 0) { env->size = 16; env->ptr = malloc(env->size * sizeof(*env->ptr)); - } else if (env->size == env->used) { + } else if (env->size == env->used) { env->size += 16; env->ptr = realloc(env->ptr, env->size * sizeof(*env->ptr)); } - + b->ptr[i] = '\0'; env->ptr[env->used++] = start; - + start = b->ptr + i + 1; break; default: @@ -770,7 +844,7 @@ static int parse_binpath(char_array *env, buffer *b) { return 0; } -static int fcgi_spawn_connection(server *srv, +static int fcgi_spawn_connection(server *srv, plugin_data *p, fcgi_extension_host *host, fcgi_proc *proc) { @@ -782,33 +856,37 @@ static int fcgi_spawn_connection(server *srv, #endif struct sockaddr_in fcgi_addr_in; struct sockaddr *fcgi_addr; - + socklen_t servlen; - + #ifndef HAVE_FORK return -1; #endif - + if (p->conf.debug) { log_error_write(srv, __FILE__, __LINE__, "sdb", - "new proc, socket:", proc->port, proc->socket); + "new proc, socket:", proc->port, proc->unixsocket); } - - if (!buffer_is_empty(proc->socket)) { + + if (!buffer_is_empty(proc->unixsocket)) { memset(&fcgi_addr, 0, sizeof(fcgi_addr)); - + #ifdef HAVE_SYS_UN_H fcgi_addr_un.sun_family = AF_UNIX; - strcpy(fcgi_addr_un.sun_path, proc->socket->ptr); - + strcpy(fcgi_addr_un.sun_path, proc->unixsocket->ptr); + #ifdef SUN_LEN servlen = SUN_LEN(&fcgi_addr_un); #else /* stevens says: */ - servlen = proc->socket->used - 1 + sizeof(fcgi_addr_un.sun_family); + servlen = proc->unixsocket->used + sizeof(fcgi_addr_un.sun_family); #endif socket_type = AF_UNIX; fcgi_addr = (struct sockaddr *) &fcgi_addr_un; + + buffer_copy_string_len(proc->connection_name, CONST_STR_LEN("unix:")); + buffer_append_string_buffer(proc->connection_name, proc->unixsocket); + #else log_error_write(srv, __FILE__, __LINE__, "s", "ERROR: Unix Domain sockets are not supported."); @@ -816,103 +894,112 @@ static int fcgi_spawn_connection(server *srv, #endif } else { fcgi_addr_in.sin_family = AF_INET; - + if (buffer_is_empty(host->host)) { - fcgi_addr_in.sin_addr.s_addr = htonl(INADDR_ANY); + fcgi_addr_in.sin_addr.s_addr = htonl(INADDR_LOOPBACK); } else { struct hostent *he; - - /* set a usefull default */ - fcgi_addr_in.sin_addr.s_addr = htonl(INADDR_ANY); - - + + /* set a useful default */ + fcgi_addr_in.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + + if (NULL == (he = gethostbyname(host->host->ptr))) { - log_error_write(srv, __FILE__, __LINE__, - "sdb", "gethostbyname failed: ", + log_error_write(srv, __FILE__, __LINE__, + "sdb", "gethostbyname failed: ", h_errno, host->host); return -1; } - + if (he->h_addrtype != AF_INET) { log_error_write(srv, __FILE__, __LINE__, "sd", "addr-type != AF_INET: ", he->h_addrtype); return -1; } - + if (he->h_length != sizeof(struct in_addr)) { log_error_write(srv, __FILE__, __LINE__, "sd", "addr-length != sizeof(in_addr): ", he->h_length); return -1; } - + memcpy(&(fcgi_addr_in.sin_addr.s_addr), he->h_addr_list[0], he->h_length); - + } fcgi_addr_in.sin_port = htons(proc->port); servlen = sizeof(fcgi_addr_in); - + socket_type = AF_INET; fcgi_addr = (struct sockaddr *) &fcgi_addr_in; + + buffer_copy_string_len(proc->connection_name, CONST_STR_LEN("tcp:")); + if (!buffer_is_empty(host->host)) { + buffer_append_string_buffer(proc->connection_name, host->host); + } else { + buffer_append_string_len(proc->connection_name, CONST_STR_LEN("localhost")); + } + buffer_append_string_len(proc->connection_name, CONST_STR_LEN(":")); + buffer_append_long(proc->connection_name, proc->port); } - + if (-1 == (fcgi_fd = socket(socket_type, SOCK_STREAM, 0))) { - log_error_write(srv, __FILE__, __LINE__, "ss", + log_error_write(srv, __FILE__, __LINE__, "ss", "failed:", strerror(errno)); return -1; } - + if (-1 == connect(fcgi_fd, fcgi_addr, servlen)) { - /* server is not up, spawn in */ + /* server is not up, spawn it */ pid_t child; int val; - - if (!buffer_is_empty(proc->socket)) { - unlink(proc->socket->ptr); + + if (errno != ENOENT && + !buffer_is_empty(proc->unixsocket)) { + unlink(proc->unixsocket->ptr); } - + close(fcgi_fd); - + /* reopen socket */ if (-1 == (fcgi_fd = socket(socket_type, SOCK_STREAM, 0))) { - log_error_write(srv, __FILE__, __LINE__, "ss", + log_error_write(srv, __FILE__, __LINE__, "ss", "socket failed:", strerror(errno)); return -1; } - + val = 1; if (setsockopt(fcgi_fd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val)) < 0) { - log_error_write(srv, __FILE__, __LINE__, "ss", + log_error_write(srv, __FILE__, __LINE__, "ss", "socketsockopt failed:", strerror(errno)); return -1; } - + /* create socket */ if (-1 == bind(fcgi_fd, fcgi_addr, servlen)) { - log_error_write(srv, __FILE__, __LINE__, "sbds", - "bind failed for:", - proc->socket, - proc->port, + log_error_write(srv, __FILE__, __LINE__, "sbs", + "bind failed for:", + proc->connection_name, strerror(errno)); return -1; } - + if (-1 == listen(fcgi_fd, 1024)) { - log_error_write(srv, __FILE__, __LINE__, "ss", + log_error_write(srv, __FILE__, __LINE__, "ss", "listen failed:", strerror(errno)); return -1; } - -#ifdef HAVE_FORK + +#ifdef HAVE_FORK switch ((child = fork())) { case 0: { size_t i = 0; char *c; char_array env; char_array arg; - + /* create environment */ env.ptr = NULL; env.size = 0; env.used = 0; - + arg.ptr = NULL; arg.size = 0; arg.used = 0; @@ -922,18 +1009,18 @@ static int fcgi_spawn_connection(server *srv, dup2(fcgi_fd, FCGI_LISTENSOCK_FILENO); close(fcgi_fd); } - + /* we don't need the client socket */ for (i = 3; i < 256; i++) { close(i); } - + /* build clean environment */ if (host->bin_env_copy->used) { for (i = 0; i < host->bin_env_copy->used; i++) { data_string *ds = (data_string *)host->bin_env_copy->data[i]; char *ge; - + if (NULL != (ge = getenv(ds->value->ptr))) { env_add(&env, CONST_BUF_LEN(ds->value), ge, strlen(ge)); } @@ -941,39 +1028,39 @@ static int fcgi_spawn_connection(server *srv, } else { for (i = 0; environ[i]; i++) { char *eq; - + if (NULL != (eq = strchr(environ[i], '='))) { env_add(&env, environ[i], eq - environ[i], eq+1, strlen(eq+1)); } } } - + /* create environment */ for (i = 0; i < host->bin_env->used; i++) { data_string *ds = (data_string *)host->bin_env->data[i]; - + env_add(&env, CONST_BUF_LEN(ds->key), CONST_BUF_LEN(ds->value)); } - + for (i = 0; i < env.used; i++) { /* search for PHP_FCGI_CHILDREN */ if (0 == strncmp(env.ptr[i], "PHP_FCGI_CHILDREN=", sizeof("PHP_FCGI_CHILDREN=") - 1)) break; } - + /* not found, add a default */ if (i == env.used) { env_add(&env, CONST_STR_LEN("PHP_FCGI_CHILDREN"), CONST_STR_LEN("1")); } - + env.ptr[env.used] = NULL; parse_binpath(&arg, host->bin_path); - + /* chdir into the base of the bin-path, * search for the last / */ if (NULL != (c = strrchr(arg.ptr[0], '/'))) { *c = '\0'; - + /* change to the physical directory */ if (-1 == chdir(arg.ptr[0])) { *c = '/'; @@ -982,15 +1069,16 @@ static int fcgi_spawn_connection(server *srv, *c = '/'; } + reset_signals(); /* exec the cgi */ execve(arg.ptr[0], arg.ptr, env.ptr); - - log_error_write(srv, __FILE__, __LINE__, "sbs", - "execve failed for:", host->bin_path, strerror(errno)); - + + /* log_error_write(srv, __FILE__, __LINE__, "sbs", + "execve failed for:", host->bin_path, strerror(errno)); */ + exit(errno); - + break; } case -1: @@ -998,17 +1086,17 @@ static int fcgi_spawn_connection(server *srv, break; default: /* father */ - + /* wait */ select(0, NULL, NULL, NULL, &tv); - + switch (waitpid(child, &status, WNOHANG)) { case 0: /* child still running after timeout, good */ break; case -1: /* no PID found ? should never happen */ - log_error_write(srv, __FILE__, __LINE__, "ss", + log_error_write(srv, __FILE__, __LINE__, "ss", "pid not found:", strerror(errno)); return -1; default: @@ -1016,30 +1104,26 @@ static int fcgi_spawn_connection(server *srv, "the fastcgi-backend", host->bin_path, "failed to start:"); /* the child should not terminate at all */ if (WIFEXITED(status)) { - log_error_write(srv, __FILE__, __LINE__, "sdb", - "child exited with status", + log_error_write(srv, __FILE__, __LINE__, "sdb", + "child exited with status", WEXITSTATUS(status), host->bin_path); - log_error_write(srv, __FILE__, __LINE__, "s", - "if you try do run PHP as FastCGI backend make sure you use the FastCGI enabled version.\n" - "You can find out if it is the right one by executing 'php -v' and it should display '(cgi-fcgi)' " - "in the output, NOT (cgi) NOR (cli)\n" - "For more information check http://www.lighttpd.net/documentation/fastcgi.html#preparing-php-as-a-fastcgi-program"); log_error_write(srv, __FILE__, __LINE__, "s", - "If this is PHP on Gentoo add fastcgi to the USE flags"); + "If you're trying to run your app as a FastCGI backend, make sure you're using the FastCGI-enabled version.\n" + "If this is PHP on Gentoo, add 'fastcgi' to the USE flags."); } else if (WIFSIGNALED(status)) { - log_error_write(srv, __FILE__, __LINE__, "sd", - "terminated by signal:", + log_error_write(srv, __FILE__, __LINE__, "sd", + "terminated by signal:", WTERMSIG(status)); if (WTERMSIG(status) == 11) { - log_error_write(srv, __FILE__, __LINE__, "ss", - "to be exact: it seg-fault, crashed, died, ... you get the idea." ); log_error_write(srv, __FILE__, __LINE__, "s", - "If this is PHP try to remove the byte-code caches for now and try again."); + "to be exact: it segfaulted, crashed, died, ... you get the idea." ); + log_error_write(srv, __FILE__, __LINE__, "s", + "If this is PHP, try removing the bytecode caches for now and try again."); } } else { - log_error_write(srv, __FILE__, __LINE__, "sd", - "child died somehow:", + log_error_write(srv, __FILE__, __LINE__, "sd", + "child died somehow:", status); } return -1; @@ -1047,28 +1131,27 @@ static int fcgi_spawn_connection(server *srv, /* register process */ proc->pid = child; - proc->last_used = srv->cur_ts; proc->is_local = 1; - + break; } #endif } else { proc->is_local = 0; proc->pid = 0; - + if (p->conf.debug) { log_error_write(srv, __FILE__, __LINE__, "sb", - "(debug) socket is already used, won't spawn:", - proc->socket); + "(debug) socket is already used; won't spawn:", + proc->connection_name); } } - + proc->state = PROC_STATE_RUNNING; host->active_procs++; - + close(fcgi_fd); - + return 0; } @@ -1078,216 +1161,216 @@ SETDEFAULTS_FUNC(mod_fastcgi_set_defaults) { data_unset *du; size_t i = 0; buffer *fcgi_mode = buffer_init(); - - config_values_t cv[] = { + + config_values_t cv[] = { { "fastcgi.server", NULL, T_CONFIG_LOCAL, T_CONFIG_SCOPE_CONNECTION }, /* 0 */ - { "fastcgi.debug", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 1 */ + { "fastcgi.debug", NULL, T_CONFIG_INT , T_CONFIG_SCOPE_CONNECTION }, /* 1 */ + { "fastcgi.map-extensions", NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION }, /* 2 */ { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET } }; - + p->config_storage = calloc(1, srv->config_context->used * sizeof(specific_config *)); - + for (i = 0; i < srv->config_context->used; i++) { plugin_config *s; array *ca; - + s = malloc(sizeof(plugin_config)); s->exts = fastcgi_extensions_init(); s->debug = 0; - + s->ext_mapping = array_init(); + cv[0].destination = s->exts; cv[1].destination = &(s->debug); - + cv[2].destination = s->ext_mapping; + p->config_storage[i] = s; ca = ((data_config *)srv->config_context->data[i])->value; - + if (0 != config_insert_values_global(srv, ca, cv)) { return HANDLER_ERROR; } - - /* + + /* * <key> = ( ... ) */ - + if (NULL != (du = array_get_element(ca, "fastcgi.server"))) { size_t j; data_array *da = (data_array *)du; - + if (du->type != TYPE_ARRAY) { - log_error_write(srv, __FILE__, __LINE__, "sss", + log_error_write(srv, __FILE__, __LINE__, "sss", "unexpected type for key: ", "fastcgi.server", "array of strings"); - + return HANDLER_ERROR; } - - - /* - * fastcgi.server = ( "<ext>" => ( ... ), + + + /* + * fastcgi.server = ( "<ext>" => ( ... ), * "<ext>" => ( ... ) ) */ - + for (j = 0; j < da->value->used; j++) { size_t n; data_array *da_ext = (data_array *)da->value->data[j]; - + if (da->value->data[j]->type != TYPE_ARRAY) { - log_error_write(srv, __FILE__, __LINE__, "sssbs", - "unexpected type for key: ", "fastcgi.server", + log_error_write(srv, __FILE__, __LINE__, "sssbs", + "unexpected type for key: ", "fastcgi.server", "[", da->value->data[j]->key, "](string)"); - + return HANDLER_ERROR; } - - /* - * da_ext->key == name of the extension + + /* + * da_ext->key == name of the extension */ - - /* - * fastcgi.server = ( "<ext>" => - * ( "<host>" => ( ... ), + + /* + * fastcgi.server = ( "<ext>" => + * ( "<host>" => ( ... ), * "<host>" => ( ... ) - * ), + * ), * "<ext>" => ... ) */ - + for (n = 0; n < da_ext->value->used; n++) { data_array *da_host = (data_array *)da_ext->value->data[n]; - + fcgi_extension_host *host; - - config_values_t fcv[] = { + + config_values_t fcv[] = { { "host", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 0 */ { "docroot", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 1 */ { "mode", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 2 */ { "socket", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 3 */ { "bin-path", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 4 */ - + { "check-local", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 5 */ { "port", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 6 */ - { "min-procs-not-working", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 7 this is broken for now */ - { "max-procs", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 8 */ - { "max-load-per-proc", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 9 */ - { "idle-timeout", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 10 */ - { "disable-time", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 11 */ - - { "bin-environment", NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION }, /* 12 */ - { "bin-copy-environment", NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION }, /* 13 */ - - { "broken-scriptfilename", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 14 */ - { "allow-x-send-file", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 15 */ - { "strip-request-uri", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 16 */ - + { "max-procs", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 7 */ + { "disable-time", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 8 */ + + { "bin-environment", NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION }, /* 9 */ + { "bin-copy-environment", NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION }, /* 10 */ + + { "broken-scriptfilename", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 11 */ + { "allow-x-send-file", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 12 */ + { "strip-request-uri", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 13 */ + { "kill-signal", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 14 */ + { "fix-root-scriptname", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 15 */ + { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET } }; - + if (da_host->type != TYPE_ARRAY) { - log_error_write(srv, __FILE__, __LINE__, "ssSBS", - "unexpected type for key:", - "fastcgi.server", + log_error_write(srv, __FILE__, __LINE__, "ssSBS", + "unexpected type for key:", + "fastcgi.server", "[", da_host->key, "](string)"); - + return HANDLER_ERROR; } - + host = fastcgi_host_init(); - + buffer_copy_string_buffer(host->id, da_host->key); host->check_local = 1; - host->min_procs = 4; host->max_procs = 4; - host->max_load_per_proc = 1; - host->idle_timeout = 60; host->mode = FCGI_RESPONDER; - host->disable_time = 60; + host->disable_time = 1; host->break_scriptfilename_for_php = 0; host->allow_xsendfile = 0; /* handle X-LIGHTTPD-send-file */ - + host->kill_signal = SIGTERM; + host->fix_root_path_name = 0; + fcv[0].destination = host->host; fcv[1].destination = host->docroot; fcv[2].destination = fcgi_mode; fcv[3].destination = host->unixsocket; fcv[4].destination = host->bin_path; - + fcv[5].destination = &(host->check_local); fcv[6].destination = &(host->port); - fcv[7].destination = &(host->min_procs); - fcv[8].destination = &(host->max_procs); - fcv[9].destination = &(host->max_load_per_proc); - fcv[10].destination = &(host->idle_timeout); - fcv[11].destination = &(host->disable_time); - - fcv[12].destination = host->bin_env; - fcv[13].destination = host->bin_env_copy; - fcv[14].destination = &(host->break_scriptfilename_for_php); - fcv[15].destination = &(host->allow_xsendfile); - fcv[16].destination = host->strip_request_uri; - + fcv[7].destination = &(host->max_procs); + fcv[8].destination = &(host->disable_time); + + fcv[9].destination = host->bin_env; + fcv[10].destination = host->bin_env_copy; + fcv[11].destination = &(host->break_scriptfilename_for_php); + fcv[12].destination = &(host->allow_xsendfile); + fcv[13].destination = host->strip_request_uri; + fcv[14].destination = &(host->kill_signal); + fcv[15].destination = &(host->fix_root_path_name); + if (0 != config_insert_values_internal(srv, da_host->value, fcv)) { return HANDLER_ERROR; } - - if ((!buffer_is_empty(host->host) || host->port) && + + if ((!buffer_is_empty(host->host) || host->port) && !buffer_is_empty(host->unixsocket)) { - log_error_write(srv, __FILE__, __LINE__, "s", - "either host+port or socket"); - + log_error_write(srv, __FILE__, __LINE__, "sbsbsbs", + "either host/port or socket have to be set in:", + da->key, "= (", + da_ext->key, " => (", + da_host->key, " ( ..."); + return HANDLER_ERROR; } - + if (!buffer_is_empty(host->unixsocket)) { /* unix domain socket */ - - if (host->unixsocket->used > UNIX_PATH_MAX - 2) { - log_error_write(srv, __FILE__, __LINE__, "s", - "path of the unixdomain socket is too large"); + struct sockaddr_un un; + + if (host->unixsocket->used > sizeof(un.sun_path) - 2) { + log_error_write(srv, __FILE__, __LINE__, "sbsbsbs", + "unixsocket is too long in:", + da->key, "= (", + da_ext->key, " => (", + da_host->key, " ( ..."); + return HANDLER_ERROR; } } else { /* tcp/ip */ - - if (buffer_is_empty(host->host) && + + if (buffer_is_empty(host->host) && buffer_is_empty(host->bin_path)) { - log_error_write(srv, __FILE__, __LINE__, "sbbbs", - "missing key (string):", - da->key, - da_ext->key, - da_host->key, - "host"); - + log_error_write(srv, __FILE__, __LINE__, "sbsbsbs", + "host or binpath have to be set in:", + da->key, "= (", + da_ext->key, " => (", + da_host->key, " ( ..."); + return HANDLER_ERROR; } else if (host->port == 0) { - log_error_write(srv, __FILE__, __LINE__, "sbbbs", - "missing key (short):", - da->key, - da_ext->key, - da_host->key, - "port"); + log_error_write(srv, __FILE__, __LINE__, "sbsbsbs", + "port has to be set in:", + da->key, "= (", + da_ext->key, " => (", + da_host->key, " ( ..."); + return HANDLER_ERROR; } } - - if (!buffer_is_empty(host->bin_path)) { + + if (!buffer_is_empty(host->bin_path)) { /* a local socket + self spawning */ size_t pno; - /* HACK: just to make sure the adaptive spawing is disabled */ - host->min_procs = host->max_procs; - - if (host->min_procs > host->max_procs) host->max_procs = host->min_procs; - if (host->max_load_per_proc < 1) host->max_load_per_proc = 0; - if (s->debug) { - log_error_write(srv, __FILE__, __LINE__, "ssbsdsbsdsd", + log_error_write(srv, __FILE__, __LINE__, "ssbsdsbsd", "--- fastcgi spawning local", "\n\tproc:", host->bin_path, "\n\tport:", host->port, "\n\tsocket", host->unixsocket, - "\n\tmin-procs:", host->min_procs, "\n\tmax-procs:", host->max_procs); } - - for (pno = 0; pno < host->min_procs; pno++) { + + for (pno = 0; pno < host->max_procs; pno++) { fcgi_proc *proc; proc = fastcgi_process_init(); @@ -1297,19 +1380,19 @@ SETDEFAULTS_FUNC(mod_fastcgi_set_defaults) { if (buffer_is_empty(host->unixsocket)) { proc->port = host->port + pno; } else { - buffer_copy_string_buffer(proc->socket, host->unixsocket); - buffer_append_string(proc->socket, "-"); - buffer_append_long(proc->socket, pno); + buffer_copy_string_buffer(proc->unixsocket, host->unixsocket); + buffer_append_string_len(proc->unixsocket, CONST_STR_LEN("-")); + buffer_append_long(proc->unixsocket, pno); } - + if (s->debug) { log_error_write(srv, __FILE__, __LINE__, "ssdsbsdsd", "--- fastcgi spawning", "\n\tport:", host->port, "\n\tsocket", host->unixsocket, - "\n\tcurrent:", pno, "/", host->min_procs); + "\n\tcurrent:", pno, "/", host->max_procs); } - + if (fcgi_spawn_connection(srv, p, host, proc)) { log_error_write(srv, __FILE__, __LINE__, "s", "[ERROR]: spawning fcgi failed."); @@ -1317,35 +1400,34 @@ SETDEFAULTS_FUNC(mod_fastcgi_set_defaults) { } fastcgi_status_init(srv, p->statuskey, host, proc); - + proc->next = host->first; if (host->first) host->first->prev = proc; - + host->first = proc; } } else { fcgi_proc *proc; - + proc = fastcgi_process_init(); proc->id = host->num_procs++; host->max_id++; host->active_procs++; proc->state = PROC_STATE_RUNNING; - + if (buffer_is_empty(host->unixsocket)) { proc->port = host->port; } else { - buffer_copy_string_buffer(proc->socket, host->unixsocket); + buffer_copy_string_buffer(proc->unixsocket, host->unixsocket); } - + fastcgi_status_init(srv, p->statuskey, host, proc); host->first = proc; - - host->min_procs = 1; + host->max_procs = 1; } - + if (!buffer_is_empty(fcgi_mode)) { if (strcmp(fcgi_mode->ptr, "responder") == 0) { host->mode = FCGI_RESPONDER; @@ -1369,140 +1451,76 @@ SETDEFAULTS_FUNC(mod_fastcgi_set_defaults) { } } } - + buffer_free(fcgi_mode); - + return HANDLER_GO_ON; } static int fcgi_set_state(server *srv, handler_ctx *hctx, fcgi_connection_state_t state) { hctx->state = state; hctx->state_timestamp = srv->cur_ts; - - return 0; -} - -static size_t fcgi_requestid_new(server *srv, plugin_data *p) { - size_t m = 0; - size_t i; - buffer_uint *r = &(p->fcgi_request_id); - - UNUSED(srv); - - for (i = 0; i < r->used; i++) { - if (r->ptr[i] > m) m = r->ptr[i]; - } - - if (r->size == 0) { - r->size = 16; - r->ptr = malloc(sizeof(*r->ptr) * r->size); - } else if (r->used == r->size) { - r->size += 16; - r->ptr = realloc(r->ptr, sizeof(*r->ptr) * r->size); - } - - r->ptr[r->used++] = ++m; - - return m; + return 0; } -static int fcgi_requestid_del(server *srv, plugin_data *p, size_t request_id) { - size_t i; - buffer_uint *r = &(p->fcgi_request_id); - - UNUSED(srv); - for (i = 0; i < r->used; i++) { - if (r->ptr[i] == request_id) break; - } - - if (i != r->used) { - /* found */ - - if (i != r->used - 1) { - r->ptr[i] = r->ptr[r->used - 1]; - } - r->used--; - } - - return 0; -} -void fcgi_connection_close(server *srv, handler_ctx *hctx) { +static void fcgi_connection_close(server *srv, handler_ctx *hctx) { plugin_data *p; connection *con; - + if (NULL == hctx) return; - + p = hctx->plugin_data; con = hctx->remote_conn; - - if (con->mode != p->id) { - WP(); - return; - } - + if (hctx->fd != -1) { fdevent_event_del(srv->ev, &(hctx->fde_ndx), hctx->fd); fdevent_unregister(srv->ev, hctx->fd); close(hctx->fd); srv->cur_fds--; } - - if (hctx->request_id != 0) { - fcgi_requestid_del(srv, p, hctx->request_id); - } if (hctx->host && hctx->proc) { - hctx->host->load--; - if (hctx->got_proc) { /* after the connect the process gets a load */ - hctx->proc->load--; - - status_counter_dec(srv, CONST_STR_LEN("fastcgi.active-requests")); - - fastcgi_status_copy_procname(p->statuskey, hctx->host, hctx->proc); - buffer_append_string(p->statuskey, ".load"); - - status_counter_set(srv, CONST_BUF_LEN(p->statuskey), hctx->proc->load); + fcgi_proc_load_dec(srv, hctx); if (p->conf.debug) { - log_error_write(srv, __FILE__, __LINE__, "sddb", - "release proc:", - hctx->fd, - hctx->proc->pid, hctx->proc->socket); + log_error_write(srv, __FILE__, __LINE__, "ssdsbsd", + "released proc:", + "pid:", hctx->proc->pid, + "socket:", hctx->proc->connection_name, + "load:", hctx->proc->load); } } - - fcgi_proclist_sort_down(srv, hctx->host, hctx->proc); } - - handler_ctx_free(hctx); - con->plugin_ctx[p->id] = NULL; + + handler_ctx_free(srv, hctx); + con->plugin_ctx[p->id] = NULL; } static int fcgi_reconnect(server *srv, handler_ctx *hctx) { plugin_data *p = hctx->plugin_data; - - /* child died - * - * 1. - * + + /* child died + * + * 1. + * * connect was ok, connection was accepted * but the php accept loop checks after the accept if it should die or not. - * - * if yes we can only detect it at a write() - * + * + * if yes we can only detect it at a write() + * * next step is resetting this attemp and setup a connection again - * - * if we have more then 5 reconnects for the same request, die - * - * 2. - * + * + * if we have more than 5 reconnects for the same request, die + * + * 2. + * * we have a connection but the child died by some other reason - * + * */ if (hctx->fd != -1) { @@ -1510,55 +1528,73 @@ static int fcgi_reconnect(server *srv, handler_ctx *hctx) { fdevent_unregister(srv->ev, hctx->fd); close(hctx->fd); srv->cur_fds--; + hctx->fd = -1; } - - fcgi_requestid_del(srv, p, hctx->request_id); - + fcgi_set_state(srv, hctx, FCGI_STATE_INIT); - + hctx->request_id = 0; hctx->reconnects++; - - if (p->conf.debug) { - log_error_write(srv, __FILE__, __LINE__, "sddb", - "release proc:", - hctx->fd, - hctx->proc->pid, hctx->proc->socket); + + if (p->conf.debug > 2) { + if (hctx->proc) { + log_error_write(srv, __FILE__, __LINE__, "sdb", + "release proc for reconnect:", + hctx->proc->pid, hctx->proc->connection_name); + } else { + log_error_write(srv, __FILE__, __LINE__, "sb", + "release proc for reconnect:", + hctx->host->unixsocket); + } } - if (hctx->proc) { - hctx->proc->load--; - fcgi_proclist_sort_down(srv, hctx->host, hctx->proc); + if (hctx->proc && hctx->got_proc) { + fcgi_proc_load_dec(srv, hctx); } /* perhaps another host gives us more luck */ - hctx->host = NULL; - + fcgi_host_reset(srv, hctx); + return 0; } static handler_t fcgi_connection_reset(server *srv, connection *con, void *p_d) { plugin_data *p = p_d; - + fcgi_connection_close(srv, con->plugin_ctx[p->id]); - + return HANDLER_GO_ON; } static int fcgi_env_add(buffer *env, const char *key, size_t key_len, const char *val, size_t val_len) { size_t len; - + if (!key || !val) return -1; - + len = key_len + val_len; - + len += key_len > 127 ? 4 : 1; len += val_len > 127 ? 4 : 1; - + + if (env->used + len >= FCGI_MAX_LENGTH) { + /** + * we can't append more headers, ignore it + */ + return -1; + } + + /** + * field length can be 31bit max + * + * HINT: this can't happen as FCGI_MAX_LENGTH is only 16bit + */ + if (key_len > 0x7fffffff) key_len = 0x7fffffff; + if (val_len > 0x7fffffff) val_len = 0x7fffffff; + buffer_prepare_append(env, len); - + if (key_len > 127) { env->ptr[env->used++] = ((key_len >> 24) & 0xff) | 0x80; env->ptr[env->used++] = (key_len >> 16) & 0xff; @@ -1567,7 +1603,7 @@ static int fcgi_env_add(buffer *env, const char *key, size_t key_len, const char } else { env->ptr[env->used++] = (key_len >> 0) & 0xff; } - + if (val_len > 127) { env->ptr[env->used++] = ((val_len >> 24) & 0xff) | 0x80; env->ptr[env->used++] = (val_len >> 16) & 0xff; @@ -1576,16 +1612,18 @@ static int fcgi_env_add(buffer *env, const char *key, size_t key_len, const char } else { env->ptr[env->used++] = (val_len >> 0) & 0xff; } - + memcpy(env->ptr + env->used, key, key_len); env->used += key_len; memcpy(env->ptr + env->used, val, val_len); env->used += val_len; - + return 0; } static int fcgi_header(FCGI_Header * header, unsigned char type, size_t request_id, int contentLength, unsigned char paddingLength) { + assert(contentLength <= FCGI_MAX_LENGTH); + header->version = FCGI_VERSION_1; header->type = type; header->requestIdB0 = request_id & 0xff; @@ -1594,22 +1632,14 @@ static int fcgi_header(FCGI_Header * header, unsigned char type, size_t request_ header->contentLengthB1 = (contentLength >> 8) & 0xff; header->paddingLength = paddingLength; header->reserved = 0; - + return 0; } -/** - * - * returns - * -1 error - * 0 connected - * 1 not connected yet - */ typedef enum { - CONNECTION_UNSET, CONNECTION_OK, CONNECTION_DELAYED, /* retry after event, take same host */ - CONNECTION_OVERLOADED, /* disable for 1 seconds, take another backend */ + CONNECTION_OVERLOADED, /* disable for 1 second, take another backend */ CONNECTION_DEAD /* disable for 60 seconds, take another backend */ } connection_result_t; @@ -1620,67 +1650,89 @@ static connection_result_t fcgi_establish_connection(server *srv, handler_ctx *h struct sockaddr_un fcgi_addr_un; #endif socklen_t servlen; - + fcgi_extension_host *host = hctx->host; fcgi_proc *proc = hctx->proc; int fcgi_fd = hctx->fd; - + memset(&fcgi_addr, 0, sizeof(fcgi_addr)); - - if (!buffer_is_empty(proc->socket)) { + + if (!buffer_is_empty(proc->unixsocket)) { #ifdef HAVE_SYS_UN_H /* use the unix domain socket */ fcgi_addr_un.sun_family = AF_UNIX; - strcpy(fcgi_addr_un.sun_path, proc->socket->ptr); + strcpy(fcgi_addr_un.sun_path, proc->unixsocket->ptr); #ifdef SUN_LEN servlen = SUN_LEN(&fcgi_addr_un); #else /* stevens says: */ - servlen = proc->socket->used - 1 + sizeof(fcgi_addr_un.sun_family); + servlen = proc->unixsocket->used + sizeof(fcgi_addr_un.sun_family); #endif fcgi_addr = (struct sockaddr *) &fcgi_addr_un; + + if (buffer_is_empty(proc->connection_name)) { + /* on remote spawing we have to set the connection-name now */ + buffer_copy_string_len(proc->connection_name, CONST_STR_LEN("unix:")); + buffer_append_string_buffer(proc->connection_name, proc->unixsocket); + } #else - return -1; + return CONNECTION_DEAD; #endif } else { fcgi_addr_in.sin_family = AF_INET; - if (0 == inet_aton(host->host->ptr, &(fcgi_addr_in.sin_addr))) { - log_error_write(srv, __FILE__, __LINE__, "sbs", - "converting IP-adress failed for", host->host, - "\nBe sure to specify an IP address here"); - - return -1; + if (!buffer_is_empty(host->host)) { + if (0 == inet_aton(host->host->ptr, &(fcgi_addr_in.sin_addr))) { + log_error_write(srv, __FILE__, __LINE__, "sbs", + "converting IP address failed for", host->host, + "\nBe sure to specify an IP address here"); + + return CONNECTION_DEAD; + } + } else { + fcgi_addr_in.sin_addr.s_addr = htonl(INADDR_LOOPBACK); } fcgi_addr_in.sin_port = htons(proc->port); servlen = sizeof(fcgi_addr_in); - + fcgi_addr = (struct sockaddr *) &fcgi_addr_in; + + if (buffer_is_empty(proc->connection_name)) { + /* on remote spawing we have to set the connection-name now */ + buffer_copy_string_len(proc->connection_name, CONST_STR_LEN("tcp:")); + if (!buffer_is_empty(host->host)) { + buffer_append_string_buffer(proc->connection_name, host->host); + } else { + buffer_append_string_len(proc->connection_name, CONST_STR_LEN("localhost")); + } + buffer_append_string_len(proc->connection_name, CONST_STR_LEN(":")); + buffer_append_long(proc->connection_name, proc->port); + } } - + if (-1 == connect(fcgi_fd, fcgi_addr, servlen)) { - if (errno == EINPROGRESS || + if (errno == EINPROGRESS || errno == EALREADY || errno == EINTR) { - if (hctx->conf.debug) { - log_error_write(srv, __FILE__, __LINE__, "sb", - "connect delayed, will continue later:", host->host); + if (hctx->conf.debug > 2) { + log_error_write(srv, __FILE__, __LINE__, "sb", + "connect delayed; will continue later:", proc->connection_name); } - + return CONNECTION_DELAYED; } else if (errno == EAGAIN) { if (hctx->conf.debug) { - log_error_write(srv, __FILE__, __LINE__, "sd", - "This means that the you have more incoming requests than your fastcgi-backend can handle in parallel. " - "Perhaps it helps to spawn more fastcgi backend or php-children, if not decrease server.max-connections." - "The load for this fastcgi backend is:", proc->load); + log_error_write(srv, __FILE__, __LINE__, "sbsd", + "This means that you have more incoming requests than your FastCGI backend can handle in parallel." + "It might help to spawn more FastCGI backends or PHP children; if not, decrease server.max-connections." + "The load for this FastCGI backend", proc->connection_name, "is", proc->load); } return CONNECTION_OVERLOADED; } else { - log_error_write(srv, __FILE__, __LINE__, "ssdb", - "connect failed:", - strerror(errno), - proc->port, proc->socket); + log_error_write(srv, __FILE__, __LINE__, "sssb", + "connect failed:", + strerror(errno), "on", + proc->connection_name); return CONNECTION_DEAD; } @@ -1688,7 +1740,7 @@ static connection_result_t fcgi_establish_connection(server *srv, handler_ctx *h hctx->reconnects = 0; if (hctx->conf.debug > 1) { - log_error_write(srv, __FILE__, __LINE__, "sd", + log_error_write(srv, __FILE__, __LINE__, "sd", "connect succeeded: ", fcgi_fd); } @@ -1697,21 +1749,21 @@ static connection_result_t fcgi_establish_connection(server *srv, handler_ctx *h static int fcgi_env_add_request_headers(server *srv, connection *con, plugin_data *p) { size_t i; - + for (i = 0; i < con->request.headers->used; i++) { data_string *ds; - + ds = (data_string *)con->request.headers->data[i]; - + if (ds->value->used && ds->key->used) { size_t j; buffer_reset(srv->tmp_buf); - + if (0 != strcasecmp(ds->key->ptr, "CONTENT-TYPE")) { - BUFFER_COPY_STRING_CONST(srv->tmp_buf, "HTTP_"); + buffer_copy_string_len(srv->tmp_buf, CONST_STR_LEN("HTTP_")); srv->tmp_buf->used--; } - + buffer_prepare_append(srv->tmp_buf, ds->key->used + 2); for (j = 0; j < ds->key->used - 1; j++) { char c = '_'; @@ -1725,20 +1777,20 @@ static int fcgi_env_add_request_headers(server *srv, connection *con, plugin_dat srv->tmp_buf->ptr[srv->tmp_buf->used++] = c; } srv->tmp_buf->ptr[srv->tmp_buf->used++] = '\0'; - - fcgi_env_add(p->fcgi_env, CONST_BUF_LEN(srv->tmp_buf), CONST_BUF_LEN(ds->value)); + + FCGI_ENV_ADD_CHECK(fcgi_env_add(p->fcgi_env, CONST_BUF_LEN(srv->tmp_buf), CONST_BUF_LEN(ds->value)),con); } } - + for (i = 0; i < con->environment->used; i++) { data_string *ds; - + ds = (data_string *)con->environment->data[i]; - + if (ds->value->used && ds->key->used) { size_t j; buffer_reset(srv->tmp_buf); - + buffer_prepare_append(srv->tmp_buf, ds->key->used + 2); for (j = 0; j < ds->key->used - 1; j++) { char c = '_'; @@ -1752,11 +1804,11 @@ static int fcgi_env_add_request_headers(server *srv, connection *con, plugin_dat srv->tmp_buf->ptr[srv->tmp_buf->used++] = c; } srv->tmp_buf->ptr[srv->tmp_buf->used++] = '\0'; - - fcgi_env_add(p->fcgi_env, CONST_BUF_LEN(srv->tmp_buf), CONST_BUF_LEN(ds->value)); + + FCGI_ENV_ADD_CHECK(fcgi_env_add(p->fcgi_env, CONST_BUF_LEN(srv->tmp_buf), CONST_BUF_LEN(ds->value)), con); } } - + return 0; } @@ -1765,24 +1817,24 @@ static int fcgi_create_env(server *srv, handler_ctx *hctx, size_t request_id) { FCGI_BeginRequestRecord beginRecord; FCGI_Header header; buffer *b; - + char buf[32]; const char *s; #ifdef HAVE_IPV6 char b2[INET6_ADDRSTRLEN + 1]; #endif - + plugin_data *p = hctx->plugin_data; fcgi_extension_host *host= hctx->host; connection *con = hctx->remote_conn; server_socket *srv_sock = con->srv_socket; - + sock_addr our_addr; socklen_t our_addr_len; - + /* send FCGI_BEGIN_REQUEST */ - + fcgi_header(&(beginRecord.header), FCGI_BEGIN_REQUEST, request_id, sizeof(beginRecord.body), 0); beginRecord.body.roleB0 = host->mode; beginRecord.body.roleB1 = 0; @@ -1790,76 +1842,89 @@ static int fcgi_create_env(server *srv, handler_ctx *hctx, size_t request_id) { memset(beginRecord.body.reserved, 0, sizeof(beginRecord.body.reserved)); b = chunkqueue_get_append_buffer(hctx->wb); - + buffer_copy_memory(b, (const char *)&beginRecord, sizeof(beginRecord)); - + /* send FCGI_PARAMS */ buffer_prepare_copy(p->fcgi_env, 1024); - fcgi_env_add(p->fcgi_env, CONST_STR_LEN("SERVER_SOFTWARE"), CONST_STR_LEN(PACKAGE_NAME"/"PACKAGE_VERSION)); - + if (buffer_is_empty(con->conf.server_tag)) { + FCGI_ENV_ADD_CHECK(fcgi_env_add(p->fcgi_env, CONST_STR_LEN("SERVER_SOFTWARE"), CONST_STR_LEN(PACKAGE_DESC)),con) + } else { + FCGI_ENV_ADD_CHECK(fcgi_env_add(p->fcgi_env, CONST_STR_LEN("SERVER_SOFTWARE"), CONST_BUF_LEN(con->conf.server_tag)),con) + } + if (con->server_name->used) { - fcgi_env_add(p->fcgi_env, CONST_STR_LEN("SERVER_NAME"), CONST_BUF_LEN(con->server_name)); + size_t len = con->server_name->used - 1; + + if (con->server_name->ptr[0] == '[') { + const char *colon = strstr(con->server_name->ptr, "]:"); + if (colon) len = (colon + 1) - con->server_name->ptr; + } else { + const char *colon = strchr(con->server_name->ptr, ':'); + if (colon) len = colon - con->server_name->ptr; + } + + FCGI_ENV_ADD_CHECK(fcgi_env_add(p->fcgi_env, CONST_STR_LEN("SERVER_NAME"), con->server_name->ptr, len),con) } else { #ifdef HAVE_IPV6 - s = inet_ntop(srv_sock->addr.plain.sa_family, - srv_sock->addr.plain.sa_family == AF_INET6 ? + s = inet_ntop(srv_sock->addr.plain.sa_family, + srv_sock->addr.plain.sa_family == AF_INET6 ? (const void *) &(srv_sock->addr.ipv6.sin6_addr) : (const void *) &(srv_sock->addr.ipv4.sin_addr), b2, sizeof(b2)-1); #else s = inet_ntoa(srv_sock->addr.ipv4.sin_addr); #endif - fcgi_env_add(p->fcgi_env, CONST_STR_LEN("SERVER_NAME"), s, strlen(s)); + FCGI_ENV_ADD_CHECK(fcgi_env_add(p->fcgi_env, CONST_STR_LEN("SERVER_NAME"), s, strlen(s)),con) } - - fcgi_env_add(p->fcgi_env, CONST_STR_LEN("GATEWAY_INTERFACE"), CONST_STR_LEN("CGI/1.1")); - - ltostr(buf, + + FCGI_ENV_ADD_CHECK(fcgi_env_add(p->fcgi_env, CONST_STR_LEN("GATEWAY_INTERFACE"), CONST_STR_LEN("CGI/1.1")),con) + + LI_ltostr(buf, #ifdef HAVE_IPV6 ntohs(srv_sock->addr.plain.sa_family ? srv_sock->addr.ipv6.sin6_port : srv_sock->addr.ipv4.sin_port) #else ntohs(srv_sock->addr.ipv4.sin_port) #endif ); - - fcgi_env_add(p->fcgi_env, CONST_STR_LEN("SERVER_PORT"), buf, strlen(buf)); - + + FCGI_ENV_ADD_CHECK(fcgi_env_add(p->fcgi_env, CONST_STR_LEN("SERVER_PORT"), buf, strlen(buf)),con) + /* get the server-side of the connection to the client */ our_addr_len = sizeof(our_addr); - + if (-1 == getsockname(con->fd, &(our_addr.plain), &our_addr_len)) { s = inet_ntop_cache_get_ip(srv, &(srv_sock->addr)); } else { s = inet_ntop_cache_get_ip(srv, &(our_addr)); } - fcgi_env_add(p->fcgi_env, CONST_STR_LEN("SERVER_ADDR"), s, strlen(s)); - - ltostr(buf, + FCGI_ENV_ADD_CHECK(fcgi_env_add(p->fcgi_env, CONST_STR_LEN("SERVER_ADDR"), s, strlen(s)),con) + + LI_ltostr(buf, #ifdef HAVE_IPV6 ntohs(con->dst_addr.plain.sa_family ? con->dst_addr.ipv6.sin6_port : con->dst_addr.ipv4.sin_port) #else ntohs(con->dst_addr.ipv4.sin_port) #endif ); - - fcgi_env_add(p->fcgi_env, CONST_STR_LEN("REMOTE_PORT"), buf, strlen(buf)); - + + FCGI_ENV_ADD_CHECK(fcgi_env_add(p->fcgi_env, CONST_STR_LEN("REMOTE_PORT"), buf, strlen(buf)),con) + s = inet_ntop_cache_get_ip(srv, &(con->dst_addr)); - fcgi_env_add(p->fcgi_env, CONST_STR_LEN("REMOTE_ADDR"), s, strlen(s)); - + FCGI_ENV_ADD_CHECK(fcgi_env_add(p->fcgi_env, CONST_STR_LEN("REMOTE_ADDR"), s, strlen(s)),con) + if (!buffer_is_empty(con->authed_user)) { - fcgi_env_add(p->fcgi_env, CONST_STR_LEN("REMOTE_USER"), - CONST_BUF_LEN(con->authed_user)); + FCGI_ENV_ADD_CHECK(fcgi_env_add(p->fcgi_env, CONST_STR_LEN("REMOTE_USER"), CONST_BUF_LEN(con->authed_user)),con) } - + if (con->request.content_length > 0 && host->mode != FCGI_AUTHORIZER) { /* CGI-SPEC 6.1.2 and FastCGI spec 6.3 */ - + /* request.content_length < SSIZE_MAX, see request.c */ - ltostr(buf, con->request.content_length); - fcgi_env_add(p->fcgi_env, CONST_STR_LEN("CONTENT_LENGTH"), buf, strlen(buf)); + LI_ltostr(buf, con->request.content_length); + FCGI_ENV_ADD_CHECK(fcgi_env_add(p->fcgi_env, CONST_STR_LEN("CONTENT_LENGTH"), buf, strlen(buf)),con) } if (host->mode != FCGI_AUTHORIZER) { @@ -1870,22 +1935,22 @@ static int fcgi_create_env(server *srv, handler_ctx *hctx, size_t request_id) { * For AUTHORIZER mode these headers should be omitted. */ - fcgi_env_add(p->fcgi_env, CONST_STR_LEN("SCRIPT_NAME"), CONST_BUF_LEN(con->uri.path)); - + FCGI_ENV_ADD_CHECK(fcgi_env_add(p->fcgi_env, CONST_STR_LEN("SCRIPT_NAME"), CONST_BUF_LEN(con->uri.path)),con) + if (!buffer_is_empty(con->request.pathinfo)) { - fcgi_env_add(p->fcgi_env, CONST_STR_LEN("PATH_INFO"), CONST_BUF_LEN(con->request.pathinfo)); - + FCGI_ENV_ADD_CHECK(fcgi_env_add(p->fcgi_env, CONST_STR_LEN("PATH_INFO"), CONST_BUF_LEN(con->request.pathinfo)),con) + /* PATH_TRANSLATED is only defined if PATH_INFO is set */ - + if (!buffer_is_empty(host->docroot)) { buffer_copy_string_buffer(p->path, host->docroot); } else { - buffer_copy_string_buffer(p->path, con->physical.doc_root); + buffer_copy_string_buffer(p->path, con->physical.basedir); } buffer_append_string_buffer(p->path, con->request.pathinfo); - fcgi_env_add(p->fcgi_env, CONST_STR_LEN("PATH_TRANSLATED"), CONST_BUF_LEN(p->path)); + FCGI_ENV_ADD_CHECK(fcgi_env_add(p->fcgi_env, CONST_STR_LEN("PATH_TRANSLATED"), CONST_BUF_LEN(p->path)),con) } else { - fcgi_env_add(p->fcgi_env, CONST_STR_LEN("PATH_INFO"), CONST_STR_LEN("")); + FCGI_ENV_ADD_CHECK(fcgi_env_add(p->fcgi_env, CONST_STR_LEN("PATH_INFO"), CONST_STR_LEN("")),con) } } @@ -1898,29 +1963,29 @@ static int fcgi_create_env(server *srv, handler_ctx *hctx, size_t request_id) { */ if (!buffer_is_empty(host->docroot)) { - /* - * rewrite SCRIPT_FILENAME - * + /* + * rewrite SCRIPT_FILENAME + * */ - + buffer_copy_string_buffer(p->path, host->docroot); buffer_append_string_buffer(p->path, con->uri.path); - - fcgi_env_add(p->fcgi_env, CONST_STR_LEN("SCRIPT_FILENAME"), CONST_BUF_LEN(p->path)); - fcgi_env_add(p->fcgi_env, CONST_STR_LEN("DOCUMENT_ROOT"), CONST_BUF_LEN(host->docroot)); + + FCGI_ENV_ADD_CHECK(fcgi_env_add(p->fcgi_env, CONST_STR_LEN("SCRIPT_FILENAME"), CONST_BUF_LEN(p->path)),con) + FCGI_ENV_ADD_CHECK(fcgi_env_add(p->fcgi_env, CONST_STR_LEN("DOCUMENT_ROOT"), CONST_BUF_LEN(host->docroot)),con) } else { buffer_copy_string_buffer(p->path, con->physical.path); - - /* cgi.fix_pathinfo need a broken SCRIPT_FILENAME to find out what PATH_INFO is itself - * + + /* cgi.fix_pathinfo need a broken SCRIPT_FILENAME to find out what PATH_INFO is itself + * * see src/sapi/cgi_main.c, init_request_info() */ if (host->break_scriptfilename_for_php) { buffer_append_string_buffer(p->path, con->request.pathinfo); } - - fcgi_env_add(p->fcgi_env, CONST_STR_LEN("SCRIPT_FILENAME"), CONST_BUF_LEN(p->path)); - fcgi_env_add(p->fcgi_env, CONST_STR_LEN("DOCUMENT_ROOT"), CONST_BUF_LEN(con->physical.doc_root)); + + FCGI_ENV_ADD_CHECK(fcgi_env_add(p->fcgi_env, CONST_STR_LEN("SCRIPT_FILENAME"), CONST_BUF_LEN(p->path)),con) + FCGI_ENV_ADD_CHECK(fcgi_env_add(p->fcgi_env, CONST_STR_LEN("DOCUMENT_ROOT"), CONST_BUF_LEN(con->physical.basedir)),con) } if (host->strip_request_uri->used > 1) { @@ -1928,57 +1993,54 @@ static int fcgi_create_env(server *srv, handler_ctx *hctx, size_t request_id) { /** * /app1/index/list * - * stripping /app1 or /app1/ should lead to + * stripping /app1 or /app1/ should lead to * * /index/list * */ if ('/' != host->strip_request_uri->ptr[host->strip_request_uri->used - 2]) { /* fix the user-input to have / as last char */ - buffer_append_string(host->strip_request_uri, "/"); + buffer_append_string_len(host->strip_request_uri, CONST_STR_LEN("/")); } if (con->request.orig_uri->used >= host->strip_request_uri->used && 0 == strncmp(con->request.orig_uri->ptr, host->strip_request_uri->ptr, host->strip_request_uri->used - 1)) { /* the left is the same */ - fcgi_env_add(p->fcgi_env, CONST_STR_LEN("REQUEST_URI"), + fcgi_env_add(p->fcgi_env, CONST_STR_LEN("REQUEST_URI"), con->request.orig_uri->ptr + (host->strip_request_uri->used - 2), - con->request.orig_uri->used - (host->strip_request_uri->used - 2)); + con->request.orig_uri->used - (host->strip_request_uri->used - 2) - 1); } else { - fcgi_env_add(p->fcgi_env, CONST_STR_LEN("REQUEST_URI"), CONST_BUF_LEN(con->request.orig_uri)); + FCGI_ENV_ADD_CHECK(fcgi_env_add(p->fcgi_env, CONST_STR_LEN("REQUEST_URI"), CONST_BUF_LEN(con->request.orig_uri)),con) } } else { - fcgi_env_add(p->fcgi_env, CONST_STR_LEN("REQUEST_URI"), CONST_BUF_LEN(con->request.orig_uri)); + FCGI_ENV_ADD_CHECK(fcgi_env_add(p->fcgi_env, CONST_STR_LEN("REQUEST_URI"), CONST_BUF_LEN(con->request.orig_uri)),con) } if (!buffer_is_equal(con->request.uri, con->request.orig_uri)) { - fcgi_env_add(p->fcgi_env, CONST_STR_LEN("REDIRECT_URI"), CONST_BUF_LEN(con->request.uri)); + FCGI_ENV_ADD_CHECK(fcgi_env_add(p->fcgi_env, CONST_STR_LEN("REDIRECT_URI"), CONST_BUF_LEN(con->request.uri)),con) } if (!buffer_is_empty(con->uri.query)) { - fcgi_env_add(p->fcgi_env, CONST_STR_LEN("QUERY_STRING"), CONST_BUF_LEN(con->uri.query)); + FCGI_ENV_ADD_CHECK(fcgi_env_add(p->fcgi_env, CONST_STR_LEN("QUERY_STRING"), CONST_BUF_LEN(con->uri.query)),con) } else { - fcgi_env_add(p->fcgi_env, CONST_STR_LEN("QUERY_STRING"), CONST_STR_LEN("")); + FCGI_ENV_ADD_CHECK(fcgi_env_add(p->fcgi_env, CONST_STR_LEN("QUERY_STRING"), CONST_STR_LEN("")),con) } - + s = get_http_method_name(con->request.http_method); - fcgi_env_add(p->fcgi_env, CONST_STR_LEN("REQUEST_METHOD"), s, strlen(s)); - fcgi_env_add(p->fcgi_env, CONST_STR_LEN("REDIRECT_STATUS"), CONST_STR_LEN("200")); /* if php is compiled with --force-redirect */ + FCGI_ENV_ADD_CHECK(fcgi_env_add(p->fcgi_env, CONST_STR_LEN("REQUEST_METHOD"), s, strlen(s)),con) + FCGI_ENV_ADD_CHECK(fcgi_env_add(p->fcgi_env, CONST_STR_LEN("REDIRECT_STATUS"), CONST_STR_LEN("200")),con) /* if php is compiled with --force-redirect */ s = get_http_version_name(con->request.http_version); - fcgi_env_add(p->fcgi_env, CONST_STR_LEN("SERVER_PROTOCOL"), s, strlen(s)); - -#ifdef USE_OPENSSL - if (srv_sock->is_ssl) { - fcgi_env_add(p->fcgi_env, CONST_STR_LEN("HTTPS"), CONST_STR_LEN("on")); + FCGI_ENV_ADD_CHECK(fcgi_env_add(p->fcgi_env, CONST_STR_LEN("SERVER_PROTOCOL"), s, strlen(s)),con) + + if (srv_sock->is_ssl || srv_sock->is_proxy_ssl) { + FCGI_ENV_ADD_CHECK(fcgi_env_add(p->fcgi_env, CONST_STR_LEN("HTTPS"), CONST_STR_LEN("on")),con) } -#endif - - - fcgi_env_add_request_headers(srv, con, p); - + + FCGI_ENV_ADD_CHECK(fcgi_env_add_request_headers(srv, con, p), con); + fcgi_header(&(header), FCGI_PARAMS, request_id, p->fcgi_env->used, 0); buffer_append_memory(b, (const char *)&header, sizeof(header)); buffer_append_memory(b, (const char *)p->fcgi_env->ptr, p->fcgi_env->used); - + fcgi_header(&(header), FCGI_PARAMS, request_id, 0, 0); buffer_append_memory(b, (const char *)&header, sizeof(header)); @@ -1996,9 +2058,9 @@ static int fcgi_create_env(server *srv, handler_ctx *hctx, size_t request_id) { off_t written = 0; off_t weHave = 0; - /* we announce toWrite octects - * now take all the request_content chunk that we need to fill this request - * */ + /* we announce toWrite octets + * now take all the request_content chunks that we need to fill this request + * */ b = chunkqueue_get_append_buffer(hctx->wb); fcgi_header(&(header), FCGI_STDIN, request_id, weWant, 0); @@ -2006,12 +2068,12 @@ static int fcgi_create_env(server *srv, handler_ctx *hctx, size_t request_id) { hctx->wb->bytes_in += sizeof(header); if (p->conf.debug > 10) { - fprintf(stderr, "%s.%d: tosend: %lld / %lld\n", __FILE__, __LINE__, offset, req_cq->bytes_in); + log_error_write(srv, __FILE__, __LINE__, "soso", "tosend:", offset, "/", req_cq->bytes_in); } for (written = 0; written != weWant; ) { if (p->conf.debug > 10) { - fprintf(stderr, "%s.%d: chunk: %lld / %lld\n", __FILE__, __LINE__, written, weWant); + log_error_write(srv, __FILE__, __LINE__, "soso", "chunk:", written, "/", weWant); } switch (req_c->type) { @@ -2021,16 +2083,14 @@ static int fcgi_create_env(server *srv, handler_ctx *hctx, size_t request_id) { if (weHave > weWant - written) weHave = weWant - written; if (p->conf.debug > 10) { - fprintf(stderr, "%s.%d: sending %lld bytes from (%lld / %lld) %s\n", - __FILE__, __LINE__, - weHave, - req_c->offset, - req_c->file.length, - req_c->file.name->ptr); + log_error_write(srv, __FILE__, __LINE__, "soSosOsb", + "sending", weHave, "bytes from (", + req_c->offset, "/", req_c->file.length, ")", + req_c->file.name); } assert(weHave != 0); - + chunkqueue_append_file(hctx->wb, req_c->file.name, req_c->offset, weHave); req_c->offset += weHave; @@ -2045,7 +2105,7 @@ static int fcgi_create_env(server *srv, handler_ctx *hctx, size_t request_id) { * - we reference the tempfile from the request-content-queue several times * if the req_c is larger than FCGI_MAX_LENGTH * - we can't simply cleanup the request-content-queue as soon as possible - * as it would remove the tempfiles + * as it would remove the tempfiles * - the idea is to 'steal' the tempfiles and attach the is_temp flag to the last * referencing chunk of the fastcgi-write-queue * @@ -2055,7 +2115,7 @@ static int fcgi_create_env(server *srv, handler_ctx *hctx, size_t request_id) { chunk *c; if (p->conf.debug > 10) { - fprintf(stderr, "%s.%d: next chunk\n", __FILE__, __LINE__); + log_error_write(srv, __FILE__, __LINE__, "s", "next chunk"); } c = hctx->wb->last; @@ -2082,10 +2142,10 @@ static int fcgi_create_env(server *srv, handler_ctx *hctx, size_t request_id) { req_c->offset += weHave; req_cq->bytes_out += weHave; written += weHave; - + hctx->wb->bytes_in += weHave; - if (req_c->offset == req_c->mem->used - 1) { + if (req_c->offset == (off_t) req_c->mem->used - 1) { chunkqueue_remove_finished_chunks(req_cq); req_c = req_cq->first; @@ -2096,12 +2156,12 @@ static int fcgi_create_env(server *srv, handler_ctx *hctx, size_t request_id) { break; } } - + b->used++; /* add virtual \0 */ offset += weWant; } } - + b = chunkqueue_get_append_buffer(hctx->wb); /* terminate STDIN */ fcgi_header(&(header), FCGI_STDIN, request_id, 0, 0); @@ -2110,65 +2170,53 @@ static int fcgi_create_env(server *srv, handler_ctx *hctx, size_t request_id) { hctx->wb->bytes_in += sizeof(header); -#if 0 - for (i = 0; i < hctx->write_buffer->used; i++) { - fprintf(stderr, "%02x ", hctx->write_buffer->ptr[i]); - if ((i+1) % 16 == 0) { - size_t j; - for (j = i-15; j <= i; j++) { - fprintf(stderr, "%c", - isprint((unsigned char)hctx->write_buffer->ptr[j]) ? hctx->write_buffer->ptr[j] : '.'); - } - fprintf(stderr, "\n"); - } - } -#endif - return 0; } static int fcgi_response_parse(server *srv, connection *con, plugin_data *p, buffer *in) { char *s, *ns; - + handler_ctx *hctx = con->plugin_ctx[p->id]; fcgi_extension_host *host= hctx->host; - + int have_sendfile2 = 0; + off_t sendfile2_content_length = 0; + UNUSED(srv); buffer_copy_string_buffer(p->parse_response, in); - + /* search for \n */ for (s = p->parse_response->ptr; NULL != (ns = strchr(s, '\n')); s = ns + 1) { char *key, *value; int key_len; - data_string *ds; - + data_string *ds = NULL; + /* a good day. Someone has read the specs and is sending a \r\n to us */ - + if (ns > p->parse_response->ptr && *(ns-1) == '\r') { *(ns-1) = '\0'; } - + ns[0] = '\0'; - + key = s; if (NULL == (value = strchr(s, ':'))) { /* we expect: "<key>: <value>\n" */ continue; } - + key_len = value - key; - + value++; /* strip WS */ while (*value == ' ' || *value == '\t') value++; - + if (host->mode != FCGI_AUTHORIZER || !(con->http_status == 0 || con->http_status == 200)) { /* authorizers shouldn't affect the response headers sent back to the client */ - + /* don't forward Status: */ if (0 != strncasecmp(key, "Status", key_len)) { if (NULL == (ds = (data_string *)array_get_unused_element(con->response.headers, TYPE_STRING))) { @@ -2176,11 +2224,11 @@ static int fcgi_response_parse(server *srv, connection *con, plugin_data *p, buf } buffer_copy_string_len(ds->key, key, key_len); buffer_copy_string(ds->value, value); - + array_insert_unique(con->response.headers, (data_unset *)ds); } } - + switch(key_len) { case 4: if (0 == strncasecmp(key, "Date", key_len)) { @@ -2204,11 +2252,97 @@ static int fcgi_response_parse(server *srv, connection *con, plugin_data *p, buf con->parsed_response |= HTTP_CONNECTION; } break; + case 11: + if (host->allow_xsendfile && 0 == strncasecmp(key, "X-Sendfile2", key_len)&& hctx->send_content_body) { + char *pos = value; + have_sendfile2 = 1; + + while (*pos) { + char *filename, *range; + stat_cache_entry *sce; + off_t begin_range, end_range, range_len; + + while (' ' == *pos) pos++; + if (!*pos) break; + + filename = pos; + if (NULL == (range = strchr(pos, ' '))) { + /* missing range */ + if (p->conf.debug) { + log_error_write(srv, __FILE__, __LINE__, "ss", "Couldn't find range after filename:", filename); + } + return 1; + } + buffer_copy_string_len(srv->tmp_buf, filename, range - filename); + + /* find end of range */ + for (pos = ++range; *pos && *pos != ' ' && *pos != ','; pos++) ; + + buffer_urldecode_path(srv->tmp_buf); + if (HANDLER_ERROR == stat_cache_get_entry(srv, con, srv->tmp_buf, &sce)) { + if (p->conf.debug) { + log_error_write(srv, __FILE__, __LINE__, "sb", + "send-file error: couldn't get stat_cache entry for X-Sendfile2:", + srv->tmp_buf); + } + return 1; + } else if (!S_ISREG(sce->st.st_mode)) { + if (p->conf.debug) { + log_error_write(srv, __FILE__, __LINE__, "sb", + "send-file error: wrong filetype for X-Sendfile2:", + srv->tmp_buf); + } + return 1; + } + /* found the file */ + + /* parse range */ + begin_range = 0; end_range = sce->st.st_size - 1; + { + char *rpos = NULL; + errno = 0; + begin_range = strtoll(range, &rpos, 10); + if (errno != 0 || begin_range < 0 || rpos == range) goto range_failed; + if ('-' != *rpos++) goto range_failed; + if (rpos != pos) { + range = rpos; + end_range = strtoll(range, &rpos, 10); + if (errno != 0 || end_range < 0 || rpos == range) goto range_failed; + } + if (rpos != pos) goto range_failed; + + goto range_success; + +range_failed: + if (p->conf.debug) { + log_error_write(srv, __FILE__, __LINE__, "ss", "Couldn't decode range after filename:", filename); + } + return 1; + +range_success: ; + } + + /* no parameters accepted */ + + while (*pos == ' ') pos++; + if (*pos != '\0' && *pos != ',') return 1; + + range_len = end_range - begin_range + 1; + if (range_len < 0) return 1; + if (range_len != 0) { + http_chunk_append_file(srv, con, srv->tmp_buf, begin_range, range_len); + } + sendfile2_content_length += range_len; + + if (*pos == ',') pos++; + } + } + break; case 14: if (0 == strncasecmp(key, "Content-Length", key_len)) { con->response.content_length = strtol(value, NULL, 10); con->parsed_response |= HTTP_CONTENT_LENGTH; - + if (con->response.content_length < 0) con->response.content_length = 0; } break; @@ -2216,18 +2350,38 @@ static int fcgi_response_parse(server *srv, connection *con, plugin_data *p, buf break; } } - + + if (have_sendfile2) { + data_string *dcls; + + hctx->send_content_body = 0; + joblist_append(srv, con); + + /* fix content-length */ + if (NULL == (dcls = (data_string *)array_get_unused_element(con->response.headers, TYPE_STRING))) { + dcls = data_response_init(); + } + + buffer_copy_string_len(dcls->key, "Content-Length", sizeof("Content-Length")-1); + buffer_copy_off_t(dcls->value, sendfile2_content_length); + dcls = (data_string*) array_replace(con->response.headers, (data_unset *)dcls); + if (dcls) dcls->free((data_unset*)dcls); + + con->parsed_response |= HTTP_CONTENT_LENGTH; + con->response.content_length = sendfile2_content_length; + } + /* CGI/1.1 rev 03 - 7.2.1.2 */ if ((con->parsed_response & HTTP_LOCATION) && !(con->parsed_response & HTTP_STATUS)) { con->http_status = 302; } - + return 0; } typedef struct { - buffer *b; + buffer *b; size_t len; int type; int padding; @@ -2236,8 +2390,8 @@ typedef struct { static int fastcgi_get_packet(server *srv, handler_ctx *hctx, fastcgi_response_packet *packet) { chunk * c; - size_t offset = 0; - size_t toread = 0; + size_t offset; + size_t toread; FCGI_Header *header; if (!hctx->rb->first) return -1; @@ -2248,15 +2402,22 @@ static int fastcgi_get_packet(server *srv, handler_ctx *hctx, fastcgi_response_p packet->padding = 0; packet->request_id = 0; + offset = 0; toread = 8; /* get at least the FastCGI header */ for (c = hctx->rb->first; c; c = c->next) { + size_t weHave = c->mem->used - c->offset - 1; + + if (weHave > toread) weHave = toread; + if (packet->b->used == 0) { - buffer_copy_string_len(packet->b, c->mem->ptr + c->offset, c->mem->used - c->offset - 1); + buffer_copy_string_len(packet->b, c->mem->ptr + c->offset, weHave); } else { - buffer_append_string_len(packet->b, c->mem->ptr + c->offset, c->mem->used - c->offset - 1); + buffer_append_string_len(packet->b, c->mem->ptr + c->offset, weHave); } + toread -= weHave; + offset = weHave; /* skip offset bytes in chunk for "real" data */ - if (packet->b->used >= sizeof(*header) + 1) break; + if (0 == toread) break; } if ((packet->b->used == 0) || @@ -2264,23 +2425,22 @@ static int fastcgi_get_packet(server *srv, handler_ctx *hctx, fastcgi_response_p /* no header */ buffer_free(packet->b); - log_error_write(srv, __FILE__, __LINE__, "s", "FastCGI: header too small"); + if (hctx->plugin_data->conf.debug) { + log_error_write(srv, __FILE__, __LINE__, "sdsds", "FastCGI: header too small:", packet->b->used, "bytes <", sizeof(FCGI_Header), "bytes, waiting for more data"); + } return -1; } - /* we have at least a header, now check how much me have to fetch */ + /* we have at least a header, now check how much me have to fetch */ header = (FCGI_Header *)(packet->b->ptr); - + packet->len = (header->contentLengthB0 | (header->contentLengthB1 << 8)) + header->paddingLength; packet->request_id = (header->requestIdB0 | (header->requestIdB1 << 8)); packet->type = header->type; packet->padding = header->paddingLength; - /* the first bytes in packet->b are the header */ - offset = sizeof(*header); - /* ->b should only be the content */ - buffer_copy_string(packet->b, ""); /* used == 1 */ + buffer_copy_string_len(packet->b, CONST_STR_LEN("")); /* used == 1 */ if (packet->len) { /* copy the content */ @@ -2289,15 +2449,15 @@ static int fastcgi_get_packet(server *srv, handler_ctx *hctx, fastcgi_response_p size_t weHave = c->mem->used - c->offset - offset - 1; if (weHave > weWant) weHave = weWant; - + buffer_append_string_len(packet->b, c->mem->ptr + c->offset + offset, weHave); - /* we only skipped the first 8 bytes as they are the fcgi header */ + /* we only skipped the first bytes as they belonged to the fcgi header */ offset = 0; } if (packet->b->used < packet->len + 1) { - /* we didn't got the full packet */ + /* we didn't get the full packet */ buffer_free(packet->b); return -1; @@ -2321,7 +2481,7 @@ static int fastcgi_get_packet(server *srv, handler_ctx *hctx, fastcgi_response_p } chunkqueue_remove_finished_chunks(hctx->rb); - + return 0; } @@ -2329,59 +2489,69 @@ static int fcgi_demux_response(server *srv, handler_ctx *hctx) { int fin = 0; int toread; ssize_t r; - + plugin_data *p = hctx->plugin_data; connection *con = hctx->remote_conn; int fcgi_fd = hctx->fd; fcgi_extension_host *host= hctx->host; fcgi_proc *proc = hctx->proc; - - /* - * check how much we have to read + + /* + * check how much we have to read */ if (ioctl(hctx->fd, FIONREAD, &toread)) { - log_error_write(srv, __FILE__, __LINE__, "sd", + if (errno == EAGAIN) return 0; + log_error_write(srv, __FILE__, __LINE__, "sd", "unexpected end-of-file (perhaps the fastcgi process died):", fcgi_fd); return -1; } - + /* init read-buffer */ - + if (toread > 0) { buffer *b; + chunk *cq_first = hctx->rb->first; + chunk *cq_last = hctx->rb->last; b = chunkqueue_get_append_buffer(hctx->rb); buffer_prepare_copy(b, toread + 1); /* append to read-buffer */ if (-1 == (r = read(hctx->fd, b->ptr, toread))) { - log_error_write(srv, __FILE__, __LINE__, "sds", + if (errno == EAGAIN) { + /* roll back the last chunk allocation, + and continue on next iteration */ + buffer_free(hctx->rb->last->mem); + free(hctx->rb->last); + hctx->rb->first = cq_first; + hctx->rb->last = cq_last; + return 0; + } + log_error_write(srv, __FILE__, __LINE__, "sds", "unexpected end-of-file (perhaps the fastcgi process died):", fcgi_fd, strerror(errno)); return -1; } - + /* this should be catched by the b > 0 above */ assert(r); b->used = r + 1; /* one extra for the fake \0 */ b->ptr[b->used - 1] = '\0'; } else { - log_error_write(srv, __FILE__, __LINE__, "ssdsbsbsd", + log_error_write(srv, __FILE__, __LINE__, "ssdsb", "unexpected end-of-file (perhaps the fastcgi process died):", "pid:", proc->pid, - "socket:", proc->socket, - "host:", host->host, - "port:", proc->port); - + "socket:", proc->connection_name); + return -1; } /* * parse the fastcgi packets and forward the content to the write-queue * - */ + */ while (fin == 0) { fastcgi_response_packet packet; @@ -2400,9 +2570,9 @@ static int fcgi_demux_response(server *srv, handler_ctx *hctx) { char *c; size_t blen; data_string *ds; - - /* search for header terminator - * + + /* search for header terminator + * * if we start with \r\n check if last packet terminated with \r\n * if we start with \n check if last packet terminated with \n * search for \r\n\r\n @@ -2429,7 +2599,12 @@ static int fcgi_demux_response(server *srv, handler_ctx *hctx) { } /* parse the response header */ - fcgi_response_parse(srv, con, p, hctx->response_header); + if (fcgi_response_parse(srv, con, p, hctx->response_header)) { + con->http_status = 502; + hctx->send_content_body = 0; + con->file_started = 1; + break; + } con->file_started = 1; @@ -2440,21 +2615,41 @@ static int fcgi_demux_response(server *srv, handler_ctx *hctx) { hctx->send_content_body = 0; } - if (host->allow_xsendfile && - NULL != (ds = (data_string *) array_get_element(con->response.headers, "X-LIGHTTPD-send-file"))) { + if (host->allow_xsendfile && hctx->send_content_body && + (NULL != (ds = (data_string *) array_get_element(con->response.headers, "X-LIGHTTPD-send-file")) + || NULL != (ds = (data_string *) array_get_element(con->response.headers, "X-Sendfile")))) { stat_cache_entry *sce; if (HANDLER_ERROR != stat_cache_get_entry(srv, con, ds->value, &sce)) { + data_string *dcls; + if (NULL == (dcls = (data_string *)array_get_unused_element(con->response.headers, TYPE_STRING))) { + dcls = data_response_init(); + } /* found */ - http_chunk_append_file(srv, con, ds->value, 0, sce->st.st_size); hctx->send_content_body = 0; /* ignore the content */ joblist_append(srv, con); + + buffer_copy_string_len(dcls->key, "Content-Length", sizeof("Content-Length")-1); + buffer_copy_off_t(dcls->value, sce->st.st_size); + dcls = (data_string*) array_replace(con->response.headers, (data_unset *)dcls); + if (dcls) dcls->free((data_unset*)dcls); + + con->parsed_response |= HTTP_CONTENT_LENGTH; + con->response.content_length = sce->st.st_size; + } else { + log_error_write(srv, __FILE__, __LINE__, "sb", + "send-file error: couldn't get stat_cache entry for:", + ds->value); + con->http_status = 502; + hctx->send_content_body = 0; + con->file_started = 1; + break; } } - - if (hctx->send_content_body && blen > 1) { + + if (hctx->send_content_body && blen > 1) { /* enable chunked-transfer-encoding */ if (con->request.http_version == HTTP_VERSION_1_1 && !(con->parsed_response & HTTP_CONTENT_LENGTH)) { @@ -2476,264 +2671,169 @@ static int fcgi_demux_response(server *srv, handler_ctx *hctx) { } break; case FCGI_STDERR: - log_error_write(srv, __FILE__, __LINE__, "sb", + if (packet.len == 0) break; + + log_error_write(srv, __FILE__, __LINE__, "sb", "FastCGI-stderr:", packet.b); - + break; case FCGI_END_REQUEST: con->file_finished = 1; - + if (host->mode != FCGI_AUTHORIZER || !(con->http_status == 0 || con->http_status == 200)) { - /* send chunk-end if nesseary */ + /* send chunk-end if necessary */ http_chunk_append_mem(srv, con, NULL, 0); joblist_append(srv, con); } - + fin = 1; break; default: - log_error_write(srv, __FILE__, __LINE__, "sd", + log_error_write(srv, __FILE__, __LINE__, "sd", "FastCGI: header.type not handled: ", packet.type); break; } buffer_free(packet.b); } - - return fin; -} - -int fcgi_proclist_sort_up(server *srv, fcgi_extension_host *host, fcgi_proc *proc) { - fcgi_proc *p; - - UNUSED(srv); - - /* we have been the smallest of the current list - * and we want to insert the node sorted as soon - * possible - * - * 1 0 0 0 1 1 1 - * | ^ - * | | - * +------+ - * - */ - - /* nothing to sort, only one element */ - if (host->first == proc && proc->next == NULL) return 0; - - for (p = proc; p->next && p->next->load < proc->load; p = p->next); - - /* no need to move something - * - * 1 2 2 2 3 3 3 - * ^ - * | - * + - * - */ - if (p == proc) return 0; - - if (host->first == proc) { - /* we have been the first elememt */ - - host->first = proc->next; - host->first->prev = NULL; - } - - /* disconnect proc */ - - if (proc->prev) proc->prev->next = proc->next; - if (proc->next) proc->next->prev = proc->prev; - - /* proc should be right of p */ - - proc->next = p->next; - proc->prev = p; - if (p->next) p->next->prev = proc; - p->next = proc; -#if 0 - for(p = host->first; p; p = p->next) { - log_error_write(srv, __FILE__, __LINE__, "dd", - p->pid, p->load); - } -#else - UNUSED(srv); -#endif - - return 0; -} - -int fcgi_proclist_sort_down(server *srv, fcgi_extension_host *host, fcgi_proc *proc) { - fcgi_proc *p; - - UNUSED(srv); - - /* we have been the smallest of the current list - * and we want to insert the node sorted as soon - * possible - * - * 0 0 0 0 1 0 1 - * ^ | - * | | - * +----------+ - * - * - * the basic is idea is: - * - the last active fastcgi process should be still - * in ram and is not swapped out yet - * - processes that are not reused will be killed - * after some time by the trigger-handler - * - remember it as: - * everything > 0 is hot - * all unused procs are colder the more right they are - * ice-cold processes are propably unused since more - * than 'unused-timeout', are swaped out and won't be - * reused in the next seconds anyway. - * - */ - - /* nothing to sort, only one element */ - if (host->first == proc && proc->next == NULL) return 0; - - for (p = host->first; p != proc && p->load < proc->load; p = p->next); - - - /* no need to move something - * - * 1 2 2 2 3 3 3 - * ^ - * | - * + - * - */ - if (p == proc) return 0; - - /* we have to move left. If we are already the first element - * we are done */ - if (host->first == proc) return 0; - - /* release proc */ - if (proc->prev) proc->prev->next = proc->next; - if (proc->next) proc->next->prev = proc->prev; - - /* proc should be left of p */ - proc->next = p; - proc->prev = p->prev; - if (p->prev) p->prev->next = proc; - p->prev = proc; - - if (proc->prev == NULL) host->first = proc; -#if 0 - for(p = host->first; p; p = p->next) { - log_error_write(srv, __FILE__, __LINE__, "dd", - p->pid, p->load); - } -#else - UNUSED(srv); -#endif - return 0; + return fin; } static int fcgi_restart_dead_procs(server *srv, plugin_data *p, fcgi_extension_host *host) { fcgi_proc *proc; - + for (proc = host->first; proc; proc = proc->next) { - if (p->conf.debug) { - log_error_write(srv, __FILE__, __LINE__, "sbdbdddd", - "proc:", - host->host, proc->port, - proc->socket, + int status; + + if (p->conf.debug > 2) { + log_error_write(srv, __FILE__, __LINE__, "sbdddd", + "proc:", + proc->connection_name, proc->state, proc->is_local, proc->load, proc->pid); } - - if (!proc->is_local) { - /* - * external servers might get disabled - * - * enable the server again, perhaps it is back again - */ - - if ((proc->state == PROC_STATE_DISABLED) && - (srv->cur_ts > proc->disabled_until)) { - proc->state = PROC_STATE_RUNNING; - host->active_procs++; - - fastcgi_status_copy_procname(p->statuskey, host, proc); - buffer_append_string(p->statuskey, ".disabled"); - status_counter_set(srv, CONST_BUF_LEN(p->statuskey), 0); + /* + * if the remote side is overloaded, we check back after <n> seconds + * + */ + switch (proc->state) { + case PROC_STATE_KILLED: + case PROC_STATE_UNSET: + /* this should never happen as long as adaptive spawing is disabled */ + assert(0); - log_error_write(srv, __FILE__, __LINE__, "sbdb", - "fcgi-server re-enabled:", - host->host, host->port, - host->unixsocket); - } - } else { - /* the child should not terminate at all */ - int status; - - if (proc->state == PROC_STATE_DIED_WAIT_FOR_PID) { - switch(waitpid(proc->pid, &status, WNOHANG)) { - case 0: - /* child is still alive */ - break; - case -1: - break; - default: - if (WIFEXITED(status)) { + break; + case PROC_STATE_RUNNING: + break; + case PROC_STATE_OVERLOADED: + if (srv->cur_ts <= proc->disabled_until) break; + + proc->state = PROC_STATE_RUNNING; + host->active_procs++; + + log_error_write(srv, __FILE__, __LINE__, "sbdb", + "fcgi-server re-enabled:", + host->host, host->port, + host->unixsocket); + break; + case PROC_STATE_DIED_WAIT_FOR_PID: + /* non-local procs don't have PIDs to wait for */ + if (!proc->is_local) { + proc->state = PROC_STATE_DIED; + } else { + /* the child should not terminate at all */ + + for ( ;; ) { + switch(waitpid(proc->pid, &status, WNOHANG)) { + case 0: + /* child is still alive */ + if (srv->cur_ts <= proc->disabled_until) break; + + proc->state = PROC_STATE_RUNNING; + host->active_procs++; + + log_error_write(srv, __FILE__, __LINE__, "sbdb", + "fcgi-server re-enabled:", + host->host, host->port, + host->unixsocket); + break; + case -1: + if (errno == EINTR) continue; + + log_error_write(srv, __FILE__, __LINE__, "sd", + "child died somehow, waitpid failed:", + errno); + proc->state = PROC_STATE_DIED; + break; + default: + if (WIFEXITED(status)) { #if 0 - log_error_write(srv, __FILE__, __LINE__, "sdsd", + log_error_write(srv, __FILE__, __LINE__, "sdsd", "child exited, pid:", proc->pid, "status:", WEXITSTATUS(status)); #endif - } else if (WIFSIGNALED(status)) { - log_error_write(srv, __FILE__, __LINE__, "sd", - "child signaled:", + } else if (WIFSIGNALED(status)) { + log_error_write(srv, __FILE__, __LINE__, "sd", + "child signaled:", WTERMSIG(status)); - } else { - log_error_write(srv, __FILE__, __LINE__, "sd", - "child died somehow:", + } else { + log_error_write(srv, __FILE__, __LINE__, "sd", + "child died somehow:", status); + } + + proc->state = PROC_STATE_DIED; + break; } - - proc->state = PROC_STATE_DIED; break; } } - - /* - * local servers might died, but we restart them - * - */ - if (proc->state == PROC_STATE_DIED && - proc->load == 0) { + + /* fall through if we have a dead proc now */ + if (proc->state != PROC_STATE_DIED) break; + + case PROC_STATE_DIED: + /* local procs get restarted by us, + * remote ones hopefully by the admin */ + + if (!buffer_is_empty(host->bin_path)) { + /* we still have connections bound to this proc, + * let them terminate first */ + if (proc->load != 0) break; + /* restart the child */ - + if (p->conf.debug) { - log_error_write(srv, __FILE__, __LINE__, "ssdsbsdsd", + log_error_write(srv, __FILE__, __LINE__, "ssbsdsd", "--- fastcgi spawning", - "\n\tport:", host->port, - "\n\tsocket", host->unixsocket, - "\n\tcurrent:", 1, "/", host->min_procs); + "\n\tsocket", proc->connection_name, + "\n\tcurrent:", 1, "/", host->max_procs); } - + if (fcgi_spawn_connection(srv, p, host, proc)) { log_error_write(srv, __FILE__, __LINE__, "s", "ERROR: spawning fcgi failed."); return HANDLER_ERROR; } - - fcgi_proclist_sort_down(srv, host, proc); + } else { + if (srv->cur_ts <= proc->disabled_until) break; + + proc->state = PROC_STATE_RUNNING; + host->active_procs++; + + log_error_write(srv, __FILE__, __LINE__, "sb", + "fcgi-server re-enabled:", + proc->connection_name); } + break; } } - + return 0; } @@ -2741,18 +2841,22 @@ static handler_t fcgi_write_request(server *srv, handler_ctx *hctx) { plugin_data *p = hctx->plugin_data; fcgi_extension_host *host= hctx->host; connection *con = hctx->remote_conn; - + fcgi_proc *proc; + int ret; - /* sanity check */ - if (!host || - ((!host->host->used || !host->port) && !host->unixsocket->used)) { - log_error_write(srv, __FILE__, __LINE__, "sxddd", - "write-req: error", - host, - host->host->used, - host->port, - host->unixsocket->used); + /* sanity check: + * - host != NULL + * - either: + * - tcp socket (do not check host->host->uses, as it may be not set which means INADDR_LOOPBACK) + * - unix socket + */ + if (!host) { + log_error_write(srv, __FILE__, __LINE__, "s", "fatal error: host = NULL"); + return HANDLER_ERROR; + } + if ((!host->port && !host->unixsocket->used)) { + log_error_write(srv, __FILE__, __LINE__, "s", "fatal error: neither host->port nor host->unixsocket is set"); return HANDLER_ERROR; } @@ -2760,33 +2864,39 @@ static handler_t fcgi_write_request(server *srv, handler_ctx *hctx) { if (hctx->state == FCGI_STATE_CONNECT_DELAYED) { int socket_error; socklen_t socket_error_len = sizeof(socket_error); - + /* try to finish the connect() */ if (0 != getsockopt(hctx->fd, SOL_SOCKET, SO_ERROR, &socket_error, &socket_error_len)) { - log_error_write(srv, __FILE__, __LINE__, "ss", + log_error_write(srv, __FILE__, __LINE__, "ss", "getsockopt failed:", strerror(errno)); - + + fcgi_host_disable(srv, hctx); + return HANDLER_ERROR; } if (socket_error != 0) { if (!hctx->proc->is_local || p->conf.debug) { /* local procs get restarted */ - - log_error_write(srv, __FILE__, __LINE__, "sssd", - "establishing connection failed:", strerror(socket_error), - "port:", hctx->proc->port); + + log_error_write(srv, __FILE__, __LINE__, "sssb", + "establishing connection failed:", strerror(socket_error), + "socket:", hctx->proc->connection_name); } - - hctx->proc->disabled_until = srv->cur_ts + 10; - + + fcgi_host_disable(srv, hctx); + log_error_write(srv, __FILE__, __LINE__, "sdssdsd", + "backend is overloaded; we'll disable it for", hctx->host->disable_time, "seconds and send the request to another backend instead:", + "reconnects:", hctx->reconnects, + "load:", host->load); + fastcgi_status_copy_procname(p->statuskey, hctx->host, hctx->proc); - buffer_append_string(p->statuskey, ".died"); + buffer_append_string_len(p->statuskey, CONST_STR_LEN(".died")); status_counter_inc(srv, CONST_BUF_LEN(p->statuskey)); - + return HANDLER_ERROR; } - /* go on with preparing the request */ + /* go on with preparing the request */ hctx->state = FCGI_STATE_PREPARE_WRITE; } @@ -2797,89 +2907,105 @@ static handler_t fcgi_write_request(server *srv, handler_ctx *hctx) { break; case FCGI_STATE_INIT: /* do we have a running process for this host (max-procs) ? */ + hctx->proc = NULL; + + for (proc = hctx->host->first; + proc && proc->state != PROC_STATE_RUNNING; + proc = proc->next); - for (hctx->proc = hctx->host->first; - hctx->proc && hctx->proc->state != PROC_STATE_RUNNING; - hctx->proc = hctx->proc->next); - - /* all childs are dead */ - if (hctx->proc == NULL) { + /* all children are dead */ + if (proc == NULL) { hctx->fde_ndx = -1; - + return HANDLER_ERROR; } + hctx->proc = proc; + + /* check the other procs if they have a lower load */ + for (proc = proc->next; proc; proc = proc->next) { + if (proc->state != PROC_STATE_RUNNING) continue; + if (proc->load < hctx->proc->load) hctx->proc = proc; + } + ret = host->unixsocket->used ? AF_UNIX : AF_INET; - + if (-1 == (hctx->fd = socket(ret, SOCK_STREAM, 0))) { if (errno == EMFILE || errno == EINTR) { - log_error_write(srv, __FILE__, __LINE__, "sd", + log_error_write(srv, __FILE__, __LINE__, "sd", "wait for fd at connection:", con->fd); - + return HANDLER_WAIT_FOR_FD; } - - log_error_write(srv, __FILE__, __LINE__, "ssdd", + + log_error_write(srv, __FILE__, __LINE__, "ssdd", "socket failed:", strerror(errno), srv->cur_fds, srv->max_fds); return HANDLER_ERROR; } hctx->fde_ndx = -1; - + srv->cur_fds++; - + fdevent_register(srv->ev, hctx->fd, fcgi_handle_fdevent, hctx); - + if (-1 == fdevent_fcntl_set(srv->ev, hctx->fd)) { - log_error_write(srv, __FILE__, __LINE__, "ss", + log_error_write(srv, __FILE__, __LINE__, "ss", "fcntl failed:", strerror(errno)); - + return HANDLER_ERROR; } - + if (hctx->proc->is_local) { hctx->pid = hctx->proc->pid; } - + switch (fcgi_establish_connection(srv, hctx)) { case CONNECTION_DELAYED: /* connection is in progress, wait for an event and call getsockopt() below */ - - fdevent_event_add(srv->ev, &(hctx->fde_ndx), hctx->fd, FDEVENT_OUT); - + + fdevent_event_set(srv->ev, &(hctx->fde_ndx), hctx->fd, FDEVENT_OUT); + fcgi_set_state(srv, hctx, FCGI_STATE_CONNECT_DELAYED); return HANDLER_WAIT_FOR_EVENT; case CONNECTION_OVERLOADED: /* cool down the backend, it is overloaded * -> EAGAIN */ - log_error_write(srv, __FILE__, __LINE__, "ssdsdb", - "backend is overloaded, we disable it for a 2 seconds and send the request to another backend instead:" - "reconnects:", hctx->reconnects, - "load:", host->load, - host->unixsocket); - + if (hctx->host->disable_time) { + log_error_write(srv, __FILE__, __LINE__, "sdssdsd", + "backend is overloaded; we'll disable it for", hctx->host->disable_time, "seconds and send the request to another backend instead:", + "reconnects:", hctx->reconnects, + "load:", host->load); - hctx->proc->disabled_until = srv->cur_ts + 2; + hctx->proc->disabled_until = srv->cur_ts + hctx->host->disable_time; + if (hctx->proc->state == PROC_STATE_RUNNING) hctx->host->active_procs--; + hctx->proc->state = PROC_STATE_OVERLOADED; + } fastcgi_status_copy_procname(p->statuskey, hctx->host, hctx->proc); - buffer_append_string(p->statuskey, ".overloaded"); + buffer_append_string_len(p->statuskey, CONST_STR_LEN(".overloaded")); status_counter_inc(srv, CONST_BUF_LEN(p->statuskey)); - + return HANDLER_ERROR; case CONNECTION_DEAD: /* we got a hard error from the backend like * - ECONNREFUSED for tcp-ip sockets * - ENOENT for unix-domain-sockets * - * for check if the host is back in 10 seconds + * for check if the host is back in hctx->host->disable_time seconds * */ - - hctx->proc->disabled_until = srv->cur_ts + 10; - + + fcgi_host_disable(srv, hctx); + + log_error_write(srv, __FILE__, __LINE__, "sdssdsd", + "backend died; we'll disable it for", hctx->host->disable_time, "seconds and send the request to another backend instead:", + "reconnects:", hctx->reconnects, + "load:", host->load); + fastcgi_status_copy_procname(p->statuskey, hctx->host, hctx->proc); - buffer_append_string(p->statuskey, ".died"); + buffer_append_string_len(p->statuskey, CONST_STR_LEN(".died")); status_counter_inc(srv, CONST_BUF_LEN(p->statuskey)); @@ -2888,104 +3014,67 @@ static handler_t fcgi_write_request(server *srv, handler_ctx *hctx) { /* everything is ok, go on */ fcgi_set_state(srv, hctx, FCGI_STATE_PREPARE_WRITE); - - break; - case CONNECTION_UNSET: + break; } - + case FCGI_STATE_PREPARE_WRITE: /* ok, we have the connection */ - - hctx->proc->load++; - hctx->proc->last_used = srv->cur_ts; + + fcgi_proc_load_inc(srv, hctx); hctx->got_proc = 1; - + status_counter_inc(srv, CONST_STR_LEN("fastcgi.requests")); - status_counter_inc(srv, CONST_STR_LEN("fastcgi.active-requests")); fastcgi_status_copy_procname(p->statuskey, hctx->host, hctx->proc); - buffer_append_string(p->statuskey, ".connected"); + buffer_append_string_len(p->statuskey, CONST_STR_LEN(".connected")); status_counter_inc(srv, CONST_BUF_LEN(p->statuskey)); - fastcgi_status_copy_procname(p->statuskey, hctx->host, hctx->proc); - buffer_append_string(p->statuskey, ".load"); - - status_counter_set(srv, CONST_BUF_LEN(p->statuskey), hctx->proc->load); - if (p->conf.debug) { - log_error_write(srv, __FILE__, __LINE__, "sddbdd", - "got proc:", - hctx->fd, - hctx->proc->pid, - hctx->proc->socket, - hctx->proc->port, - hctx->proc->load); + log_error_write(srv, __FILE__, __LINE__, "ssdsbsd", + "got proc:", + "pid:", hctx->proc->pid, + "socket:", hctx->proc->connection_name, + "load:", hctx->proc->load); } /* move the proc-list entry down the list */ - fcgi_proclist_sort_up(srv, hctx->host, hctx->proc); - if (hctx->request_id == 0) { - hctx->request_id = fcgi_requestid_new(srv, p); + hctx->request_id = 1; /* always use id 1 as we don't use multiplexing */ } else { - log_error_write(srv, __FILE__, __LINE__, "sd", + log_error_write(srv, __FILE__, __LINE__, "sd", "fcgi-request is already in use:", hctx->request_id); } - + /* fall through */ - fcgi_create_env(srv, hctx, hctx->request_id); - + if (-1 == fcgi_create_env(srv, hctx, hctx->request_id)) return HANDLER_ERROR; fcgi_set_state(srv, hctx, FCGI_STATE_WRITE); - /* fall through */ case FCGI_STATE_WRITE: - ret = srv->network_backend_write(srv, con, hctx->fd, hctx->wb); + ret = srv->network_backend_write(srv, con, hctx->fd, hctx->wb, MAX_WRITE_LIMIT); chunkqueue_remove_finished_chunks(hctx->wb); - + if (ret < 0) { switch(errno) { + case EPIPE: case ENOTCONN: - /* the connection got dropped after accept() - * - * this is most of the time a PHP which dies - * after PHP_FCGI_MAX_REQUESTS - * - */ - if (hctx->wb->bytes_out == 0 && - hctx->reconnects < 5) { - usleep(10000); /* take away the load of the webserver - * to let the php a chance to restart - */ - - fcgi_reconnect(srv, hctx); - - return HANDLER_WAIT_FOR_FD; - } - - /* not reconnected ... why - * - * far@#lighttpd report this for FreeBSD - * + case ECONNRESET: + /* the connection got dropped after accept() + * we don't care about that - if you accept() it, you have to handle it. */ - - log_error_write(srv, __FILE__, __LINE__, "ssdsd", - "[REPORT ME] connection was dropped after accept(). reconnect() denied:", + + log_error_write(srv, __FILE__, __LINE__, "ssosb", + "connection was dropped after accept() (perhaps the fastcgi process died),", "write-offset:", hctx->wb->bytes_out, - "reconnect attempts:", hctx->reconnects); - + "socket:", hctx->proc->connection_name); + return HANDLER_ERROR; - case EAGAIN: - case EINTR: - fdevent_event_add(srv->ev, &(hctx->fde_ndx), hctx->fd, FDEVENT_OUT); - - return HANDLER_WAIT_FOR_EVENT; default: - log_error_write(srv, __FILE__, __LINE__, "ssd", + log_error_write(srv, __FILE__, __LINE__, "ssd", "write failed:", strerror(errno), errno); - + return HANDLER_ERROR; } } @@ -2993,11 +3082,11 @@ static handler_t fcgi_write_request(server *srv, handler_ctx *hctx) { if (hctx->wb->bytes_out == hctx->wb->bytes_in) { /* we don't need the out event anymore */ fdevent_event_del(srv->ev, &(hctx->fde_ndx), hctx->fd); - fdevent_event_add(srv->ev, &(hctx->fde_ndx), hctx->fd, FDEVENT_IN); + fdevent_event_set(srv->ev, &(hctx->fde_ndx), hctx->fd, FDEVENT_IN); fcgi_set_state(srv, hctx, FCGI_STATE_READ); } else { - fdevent_event_add(srv->ev, &(hctx->fde_ndx), hctx->fd, FDEVENT_OUT); - + fdevent_event_set(srv->ev, &(hctx->fde_ndx), hctx->fd, FDEVENT_OUT); + return HANDLER_WAIT_FOR_EVENT; } @@ -3009,7 +3098,7 @@ static handler_t fcgi_write_request(server *srv, handler_ctx *hctx) { log_error_write(srv, __FILE__, __LINE__, "s", "(debug) unknown state"); return HANDLER_ERROR; } - + return HANDLER_WAIT_FOR_EVENT; } @@ -3018,58 +3107,68 @@ static handler_t fcgi_write_request(server *srv, handler_ctx *hctx) { * */ SUBREQUEST_FUNC(mod_fastcgi_handle_subrequest) { plugin_data *p = p_d; - + handler_ctx *hctx = con->plugin_ctx[p->id]; - fcgi_proc *proc; fcgi_extension_host *host; - + if (NULL == hctx) return HANDLER_GO_ON; - + /* not my job */ if (con->mode != p->id) return HANDLER_GO_ON; /* we don't have a host yet, choose one - * -> this happens in the first round + * -> this happens in the first round * and when the host died and we have to select a new one */ if (hctx->host == NULL) { size_t k; int ndx, used = -1; - /* get best server */ - for (k = 0, ndx = -1; k < hctx->ext->used; k++) { - host = hctx->ext->hosts[k]; - - /* we should have at least one proc that can do somthing */ - if (host->active_procs == 0) continue; - - if (used == -1 || host->load < used) { - used = host->load; - - ndx = k; + /* check if the next server has no load. */ + ndx = hctx->ext->last_used_ndx + 1; + if(ndx >= (int) hctx->ext->used || ndx < 0) ndx = 0; + host = hctx->ext->hosts[ndx]; + if (host->load > 0) { + /* get backend with the least load. */ + for (k = 0, ndx = -1; k < hctx->ext->used; k++) { + host = hctx->ext->hosts[k]; + + /* we should have at least one proc that can do something */ + if (host->active_procs == 0) continue; + + if (used == -1 || host->load < used) { + used = host->load; + + ndx = k; + } } } - + /* found a server */ if (ndx == -1) { /* all hosts are down */ fcgi_connection_close(srv, hctx); - + con->http_status = 500; con->mode = DIRECT; return HANDLER_FINISHED; } + hctx->ext->last_used_ndx = ndx; host = hctx->ext->hosts[ndx]; - - /* - * if check-local is disabled, use the uri.path handler - * + + /* + * if check-local is disabled, use the uri.path handler + * */ - + /* init handler-context */ - hctx->host = host; + + /* we put a connection on this host, move the other new connections to other hosts + * + * as soon as hctx->host is unassigned, decrease the load again */ + fcgi_host_assign(srv, hctx, host); hctx->proc = NULL; } else { host = hctx->host; @@ -3078,49 +3177,10 @@ SUBREQUEST_FUNC(mod_fastcgi_handle_subrequest) { /* ok, create the request */ switch(fcgi_write_request(srv, hctx)) { case HANDLER_ERROR: - proc = hctx->proc; host = hctx->host; - + if (hctx->state == FCGI_STATE_INIT || hctx->state == FCGI_STATE_CONNECT_DELAYED) { - /* connect() or getsockopt() failed, - * restart the request-handling - */ - if (proc) { - if (proc->is_local) { - - if (p->conf.debug) { - log_error_write(srv, __FILE__, __LINE__, "sbdb", - "connect() to fastcgi failed, restarting the request-handling:", - host->host, - proc->port, - proc->socket); - } - - /* - * several hctx might reference the same proc - * - * Only one of them should mark the proc as dead all the other - * ones should just take a new one. - * - * If a new proc was started with the old struct this might lead - * the mark a perfect proc as dead otherwise - * - */ - if (proc->state == PROC_STATE_RUNNING && - hctx->pid == proc->pid) { - proc->state = PROC_STATE_DIED_WAIT_FOR_PID; - } - } else { - proc->state = PROC_STATE_DISABLED; - } - host->active_procs--; - fastcgi_status_copy_procname(p->statuskey, hctx->host, hctx->proc); - buffer_append_string(p->statuskey, ".disabled"); - - status_counter_set(srv, CONST_BUF_LEN(p->statuskey), 1); - } - fcgi_restart_dead_procs(srv, p, host); /* cleanup this request and let the request handler start this request again */ @@ -3131,22 +3191,22 @@ SUBREQUEST_FUNC(mod_fastcgi_handle_subrequest) { return HANDLER_WAIT_FOR_FD; } else { fcgi_connection_close(srv, hctx); - + buffer_reset(con->physical.path); con->mode = DIRECT; - con->http_status = 500; + con->http_status = 503; joblist_append(srv, con); /* in case we come from the event-handler */ return HANDLER_FINISHED; } } else { fcgi_connection_close(srv, hctx); - + buffer_reset(con->physical.path); con->mode = DIRECT; - con->http_status = 503; + if (con->http_status != 400) con->http_status = 503; joblist_append(srv, con); /* really ? */ - + return HANDLER_FINISHED; } case HANDLER_WAIT_FOR_EVENT: @@ -3163,12 +3223,11 @@ SUBREQUEST_FUNC(mod_fastcgi_handle_subrequest) { } } -static handler_t fcgi_handle_fdevent(void *s, void *ctx, int revents) { - server *srv = (server *)s; +static handler_t fcgi_handle_fdevent(server *srv, void *ctx, int revents) { handler_ctx *hctx = ctx; connection *con = hctx->remote_conn; plugin_data *p = hctx->plugin_data; - + fcgi_proc *proc = hctx->proc; fcgi_extension_host *host= hctx->host; @@ -3178,37 +3237,39 @@ static handler_t fcgi_handle_fdevent(void *s, void *ctx, int revents) { case 0: break; case 1: - - if (host->mode == FCGI_AUTHORIZER && + + if (host->mode == FCGI_AUTHORIZER && (con->http_status == 200 || con->http_status == 0)) { /* - * If we are here in AUTHORIZER mode then a request for autorizer - * was proceeded already, and status 200 has been returned. We need - * now to handle autorized request. + * If we are here in AUTHORIZER mode then a request for authorizer + * was processed already, and status 200 has been returned. We need + * now to handle authorized request. */ buffer_copy_string_buffer(con->physical.doc_root, host->docroot); - + buffer_copy_string_buffer(con->physical.basedir, host->docroot); + buffer_copy_string_buffer(con->physical.path, host->docroot); buffer_append_string_buffer(con->physical.path, con->uri.path); fcgi_connection_close(srv, hctx); - + con->mode = DIRECT; + con->http_status = 0; con->file_started = 1; /* fcgi_extension won't touch the request afterwards */ } else { /* we are done */ fcgi_connection_close(srv, hctx); } - + joblist_append(srv, con); return HANDLER_FINISHED; case -1: if (proc->pid && proc->state != PROC_STATE_DIED) { int status; - + /* only fetch the zombie if it is not already done */ - + switch(waitpid(proc->pid, &status, WNOHANG)) { case 0: /* child is still alive */ @@ -3218,60 +3279,60 @@ static handler_t fcgi_handle_fdevent(void *s, void *ctx, int revents) { default: /* the child should not terminate at all */ if (WIFEXITED(status)) { - log_error_write(srv, __FILE__, __LINE__, "sdsd", + log_error_write(srv, __FILE__, __LINE__, "sdsd", "child exited, pid:", proc->pid, "status:", WEXITSTATUS(status)); } else if (WIFSIGNALED(status)) { - log_error_write(srv, __FILE__, __LINE__, "sd", - "child signaled:", + log_error_write(srv, __FILE__, __LINE__, "sd", + "child signaled:", WTERMSIG(status)); } else { - log_error_write(srv, __FILE__, __LINE__, "sd", - "child died somehow:", + log_error_write(srv, __FILE__, __LINE__, "sd", + "child died somehow:", status); } - + if (p->conf.debug) { - log_error_write(srv, __FILE__, __LINE__, "ssdsbsdsd", + log_error_write(srv, __FILE__, __LINE__, "ssbsdsd", "--- fastcgi spawning", - "\n\tport:", host->port, - "\n\tsocket", host->unixsocket, - "\n\tcurrent:", 1, "/", host->min_procs); + "\n\tsocket", proc->connection_name, + "\n\tcurrent:", 1, "/", host->max_procs); } - + if (fcgi_spawn_connection(srv, p, host, proc)) { - /* child died */ + /* respawning failed, retry later */ proc->state = PROC_STATE_DIED; - } else { - fcgi_proclist_sort_down(srv, host, proc); + + log_error_write(srv, __FILE__, __LINE__, "s", + "respawning failed, will retry later"); } - + break; } } if (con->file_started == 0) { - /* nothing has been send out yet, try to use another child */ - + /* nothing has been sent out yet, try to use another child */ + if (hctx->wb->bytes_out == 0 && hctx->reconnects < 5) { fcgi_reconnect(srv, hctx); - - log_error_write(srv, __FILE__, __LINE__, "ssdsd", - "response not received, request not sent, reconnecting.", - "connection-fd:", con->fd, - "fcgi-fd:", hctx->fd); - + + log_error_write(srv, __FILE__, __LINE__, "ssbsBSBs", + "response not received, request not sent", + "on socket:", proc->connection_name, + "for", con->uri.path, "?", con->uri.query, ", reconnecting"); + return HANDLER_WAIT_FOR_FD; } - - log_error_write(srv, __FILE__, __LINE__, "sosdsd", + + log_error_write(srv, __FILE__, __LINE__, "sosbsBSBs", "response not received, request sent:", hctx->wb->bytes_out, - "connection-fd:", con->fd, - "fcgi-fd:", hctx->fd); - + "on socket:", proc->connection_name, + "for", con->uri.path, "?", con->uri.query, ", closing connection"); + fcgi_connection_close(srv, hctx); - + connection_set_state(srv, con, CON_STATE_HANDLE_REQUEST); buffer_reset(con->physical.path); con->http_status = 500; @@ -3279,76 +3340,72 @@ static handler_t fcgi_handle_fdevent(void *s, void *ctx, int revents) { } else { /* response might have been already started, kill the connection */ fcgi_connection_close(srv, hctx); - - log_error_write(srv, __FILE__, __LINE__, "ssdsd", - "response already sent out, termination connection", - "connection-fd:", con->fd, - "fcgi-fd:", hctx->fd); - + + log_error_write(srv, __FILE__, __LINE__, "ssbsBSBs", + "response already sent out, but backend returned error", + "on socket:", proc->connection_name, + "for", con->uri.path, "?", con->uri.query, ", terminating connection"); + connection_set_state(srv, con, CON_STATE_ERROR); } /* */ - - + + joblist_append(srv, con); return HANDLER_FINISHED; } } - + if (revents & FDEVENT_OUT) { if (hctx->state == FCGI_STATE_CONNECT_DELAYED || hctx->state == FCGI_STATE_WRITE) { /* we are allowed to send something out - * - * 1. in a unfinished connect() call - * 2. in a unfinished write() call (long POST request) + * + * 1. in an unfinished connect() call + * 2. in an unfinished write() call (long POST request) */ return mod_fastcgi_handle_subrequest(srv, con, p); } else { - log_error_write(srv, __FILE__, __LINE__, "sd", - "got a FDEVENT_OUT and didn't know why:", + log_error_write(srv, __FILE__, __LINE__, "sd", + "got a FDEVENT_OUT and didn't know why:", hctx->state); } } - + /* perhaps this issue is already handled */ if (revents & FDEVENT_HUP) { if (hctx->state == FCGI_STATE_CONNECT_DELAYED) { /* getoptsock will catch this one (right ?) - * - * if we are in connect we might get a EINPROGRESS - * in the first call and a FDEVENT_HUP in the + * + * if we are in connect we might get an EINPROGRESS + * in the first call and an FDEVENT_HUP in the * second round - * + * * FIXME: as it is a bit ugly. - * + * */ return mod_fastcgi_handle_subrequest(srv, con, p); } else if (hctx->state == FCGI_STATE_READ && hctx->proc->port == 0) { /* FIXME: - * + * * ioctl says 8192 bytes to read from PHP and we receive directly a HUP for the socket * even if the FCGI_FIN packet is not received yet */ } else { - log_error_write(srv, __FILE__, __LINE__, "sbSBSDSd", - "error: unexpected close of fastcgi connection for", - con->uri.path, - "(no fastcgi process on host: ", - host->host, - ", port: ", - host->port, - " ?)", + log_error_write(srv, __FILE__, __LINE__, "sBSbsbsd", + "error: unexpected close of fastcgi connection for", + con->uri.path, "?", con->uri.query, + "(no fastcgi process on socket:", proc->connection_name, "?)", hctx->state); - + connection_set_state(srv, con, CON_STATE_ERROR); fcgi_connection_close(srv, hctx); joblist_append(srv, con); } } else if (revents & FDEVENT_ERR) { - log_error_write(srv, __FILE__, __LINE__, "s", + log_error_write(srv, __FILE__, __LINE__, "s", "fcgi: got a FDEVENT_ERR. Don't know why."); /* kill all connections to the fastcgi process */ @@ -3357,7 +3414,7 @@ static handler_t fcgi_handle_fdevent(void *s, void *ctx, int revents) { fcgi_connection_close(srv, hctx); joblist_append(srv, con); } - + return HANDLER_FINISHED; } #define PATCH(x) \ @@ -3365,30 +3422,33 @@ static handler_t fcgi_handle_fdevent(void *s, void *ctx, int revents) { static int fcgi_patch_connection(server *srv, connection *con, plugin_data *p) { size_t i, j; plugin_config *s = p->config_storage[0]; - + PATCH(exts); PATCH(debug); - + PATCH(ext_mapping); + /* skip the first, the global context */ for (i = 1; i < srv->config_context->used; i++) { data_config *dc = (data_config *)srv->config_context->data[i]; s = p->config_storage[i]; - + /* condition didn't match */ if (!config_check_cond(srv, con, dc)) continue; - + /* merge config */ for (j = 0; j < dc->value->used; j++) { data_unset *du = dc->value->data[j]; - + if (buffer_is_equal_string(du->key, CONST_STR_LEN("fastcgi.server"))) { PATCH(exts); } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("fastcgi.debug"))) { PATCH(debug); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("fastcgi.map-extensions"))) { + PATCH(ext_mapping); } } } - + return 0; } #undef PATCH @@ -3401,152 +3461,217 @@ static handler_t fcgi_check_extension(server *srv, connection *con, void *p_d, i buffer *fn; fcgi_extension *extension = NULL; fcgi_extension_host *host = NULL; - + + if (con->mode != DIRECT) return HANDLER_GO_ON; + /* Possibly, we processed already this request */ if (con->file_started == 1) return HANDLER_GO_ON; fn = uri_path_handler ? con->uri.path : con->physical.path; - if (fn->used == 0) { - return HANDLER_ERROR; - } - + if (buffer_is_empty(fn)) return HANDLER_GO_ON; + s_len = fn->used - 1; - + fcgi_patch_connection(srv, con, p); - - /* check if extension matches */ - for (k = 0; k < p->conf.exts->used; k++) { + + /* fastcgi.map-extensions maps extensions to existing fastcgi.server entries + * + * fastcgi.map-extensions = ( ".php3" => ".php" ) + * + * fastcgi.server = ( ".php" => ... ) + * + * */ + + /* check if extension-mapping matches */ + for (k = 0; k < p->conf.ext_mapping->used; k++) { + data_string *ds = (data_string *)p->conf.ext_mapping->data[k]; size_t ct_len; /* length of the config entry */ - - extension = p->conf.exts->exts[k]; - - if (extension->key->used == 0) continue; - - ct_len = extension->key->used - 1; - + + if (ds->key->used == 0) continue; + + ct_len = ds->key->used - 1; + if (s_len < ct_len) continue; - - /* check extension in the form "/fcgi_pattern" */ - if (*(extension->key->ptr) == '/' && strncmp(fn->ptr, extension->key->ptr, ct_len) == 0) { - break; - } else if (0 == strncmp(fn->ptr + s_len - ct_len, extension->key->ptr, ct_len)) { - /* check extension in the form ".fcg" */ + + /* found a mapping */ + if (0 == strncmp(fn->ptr + s_len - ct_len, ds->key->ptr, ct_len)) { + /* check if we know the extension */ + + /* we can reuse k here */ + for (k = 0; k < p->conf.exts->used; k++) { + extension = p->conf.exts->exts[k]; + + if (buffer_is_equal(ds->value, extension->key)) { + break; + } + } + + if (k == p->conf.exts->used) { + /* found nothign */ + extension = NULL; + } break; } } - - /* extension doesn't match */ - if (k == p->conf.exts->used) { - return HANDLER_GO_ON; + + if (extension == NULL) { + /* check if extension matches */ + for (k = 0; k < p->conf.exts->used; k++) { + size_t ct_len; /* length of the config entry */ + fcgi_extension *ext = p->conf.exts->exts[k]; + + if (ext->key->used == 0) continue; + + ct_len = ext->key->used - 1; + + /* check _url_ in the form "/fcgi_pattern" */ + if (ext->key->ptr[0] == '/') { + if ((ct_len <= con->uri.path->used -1) && + (strncmp(con->uri.path->ptr, ext->key->ptr, ct_len) == 0)) { + extension = ext; + break; + } + } else if ((ct_len <= s_len) && (0 == strncmp(fn->ptr + s_len - ct_len, ext->key->ptr, ct_len))) { + /* check extension in the form ".fcg" */ + extension = ext; + break; + } + } + /* extension doesn't match */ + if (NULL == extension) { + return HANDLER_GO_ON; + } } - /* get best server */ + /* check if we have at least one server for this extension up and running */ for (k = 0; k < extension->used; k++) { - host = extension->hosts[k]; - - /* we should have at least one proc that can do somthing */ - if (host->active_procs == 0) { - host = NULL; + fcgi_extension_host *h = extension->hosts[k]; + /* we should have at least one proc that can do something */ + if (h->active_procs == 0) { continue; } - /* we found one host that is alive */ + /* we found one host that is alive */ + host = h; break; } - + if (!host) { /* sorry, we don't have a server alive for this ext */ buffer_reset(con->physical.path); con->http_status = 500; - - log_error_write(srv, __FILE__, __LINE__, "sb", - "no fcgi-handler found for:", - fn); - + + /* only send the 'no handler' once */ + if (!extension->note_is_sent) { + extension->note_is_sent = 1; + + log_error_write(srv, __FILE__, __LINE__, "sBSbsbs", + "all handlers for", con->uri.path, "?", con->uri.query, + "on", extension->key, + "are down."); + } + return HANDLER_FINISHED; } - /* - * if check-local is disabled, use the uri.path handler - * + /* a note about no handler is not sent yet */ + extension->note_is_sent = 0; + + /* + * if check-local is disabled, use the uri.path handler + * */ - + /* init handler-context */ if (uri_path_handler) { if (host->check_local == 0) { handler_ctx *hctx; char *pathinfo; - + hctx = handler_ctx_init(); - + hctx->remote_conn = con; hctx->plugin_data = p; hctx->proc = NULL; hctx->ext = extension; - + hctx->conf.exts = p->conf.exts; hctx->conf.debug = p->conf.debug; - + con->plugin_ctx[p->id] = hctx; - + con->mode = p->id; - + if (con->conf.log_request_handling) { - log_error_write(srv, __FILE__, __LINE__, "s", + log_error_write(srv, __FILE__, __LINE__, "s", "handling it in mod_fastcgi"); } - - /* the prefix is the SCRIPT_NAME, - * everthing from start to the next slash - * this is important for check-local = "disable" - * - * if prefix = /admin.fcgi - * - * /admin.fcgi/foo/bar - * - * SCRIPT_NAME = /admin.fcgi - * PATH_INFO = /foo/bar - * - * if prefix = /fcgi-bin/ - * - * /fcgi-bin/foo/bar - * - * SCRIPT_NAME = /fcgi-bin/foo - * PATH_INFO = /bar - * - */ - - /* the rewrite is only done for /prefix/? matches */ - if (extension->key->ptr[0] == '/' && - con->uri.path->used > extension->key->used && - NULL != (pathinfo = strchr(con->uri.path->ptr + extension->key->used - 1, '/'))) { - /* rewrite uri.path and pathinfo */ - - buffer_copy_string(con->request.pathinfo, pathinfo); - - con->uri.path->used -= con->request.pathinfo->used - 1; - con->uri.path->ptr[con->uri.path->used - 1] = '\0'; + + /* do not split path info for authorizer */ + if (host->mode != FCGI_AUTHORIZER) { + /* the prefix is the SCRIPT_NAME, + * everything from start to the next slash + * this is important for check-local = "disable" + * + * if prefix = /admin.fcgi + * + * /admin.fcgi/foo/bar + * + * SCRIPT_NAME = /admin.fcgi + * PATH_INFO = /foo/bar + * + * if prefix = /fcgi-bin/ + * + * /fcgi-bin/foo/bar + * + * SCRIPT_NAME = /fcgi-bin/foo + * PATH_INFO = /bar + * + * if prefix = /, and fix-root-path-name is enable + * + * /fcgi-bin/foo/bar + * + * SCRIPT_NAME = /fcgi-bin/foo + * PATH_INFO = /bar + * + */ + + /* the rewrite is only done for /prefix/? matches */ + if (host->fix_root_path_name && extension->key->ptr[0] == '/' && extension->key->ptr[1] == '\0') { + buffer_copy_string(con->request.pathinfo, con->uri.path->ptr); + con->uri.path->used = 1; + con->uri.path->ptr[con->uri.path->used - 1] = '\0'; + } else if (extension->key->ptr[0] == '/' && + con->uri.path->used > extension->key->used && + NULL != (pathinfo = strchr(con->uri.path->ptr + extension->key->used - 1, '/'))) { + /* rewrite uri.path and pathinfo */ + + buffer_copy_string(con->request.pathinfo, pathinfo); + + con->uri.path->used -= con->request.pathinfo->used - 1; + con->uri.path->ptr[con->uri.path->used - 1] = '\0'; + } } } } else { handler_ctx *hctx; hctx = handler_ctx_init(); - + hctx->remote_conn = con; hctx->plugin_data = p; hctx->proc = NULL; hctx->ext = extension; - + hctx->conf.exts = p->conf.exts; hctx->conf.debug = p->conf.debug; - + con->plugin_ctx[p->id] = hctx; - + con->mode = p->id; - + if (con->conf.log_request_handling) { log_error_write(srv, __FILE__, __LINE__, "s", "handling it in mod_fastcgi"); } @@ -3568,19 +3693,19 @@ static handler_t fcgi_check_extension_2(server *srv, connection *con, void *p_d) JOBLIST_FUNC(mod_fastcgi_handle_joblist) { plugin_data *p = p_d; handler_ctx *hctx = con->plugin_ctx[p->id]; - + if (hctx == NULL) return HANDLER_GO_ON; if (hctx->fd != -1) { switch (hctx->state) { case FCGI_STATE_READ: - fdevent_event_add(srv->ev, &(hctx->fde_ndx), hctx->fd, FDEVENT_IN); - + fdevent_event_set(srv->ev, &(hctx->fde_ndx), hctx->fd, FDEVENT_IN); + break; case FCGI_STATE_CONNECT_DELAYED: case FCGI_STATE_WRITE: - fdevent_event_add(srv->ev, &(hctx->fde_ndx), hctx->fd, FDEVENT_OUT); - + fdevent_event_set(srv->ev, &(hctx->fde_ndx), hctx->fd, FDEVENT_OUT); + break; case FCGI_STATE_INIT: /* at reconnect */ @@ -3597,7 +3722,7 @@ JOBLIST_FUNC(mod_fastcgi_handle_joblist) { static handler_t fcgi_connection_close_callback(server *srv, connection *con, void *p_d) { plugin_data *p = p_d; - + fcgi_connection_close(srv, con->plugin_ctx[p->id]); return HANDLER_GO_ON; @@ -3606,17 +3731,17 @@ static handler_t fcgi_connection_close_callback(server *srv, connection *con, vo TRIGGER_FUNC(mod_fastcgi_handle_trigger) { plugin_data *p = p_d; size_t i, j, n; - - + + /* perhaps we should kill a connect attempt after 10-15 seconds - * - * currently we wait for the TCP timeout which is on Linux 180 seconds - * - * - * + * + * currently we wait for the TCP timeout which is 180 seconds on Linux + * + * + * */ - /* check all childs if they are still up */ + /* check all children if they are still up */ for (i = 0; i < srv->config_context->used; i++) { plugin_config *conf; @@ -3630,118 +3755,21 @@ TRIGGER_FUNC(mod_fastcgi_handle_trigger) { fcgi_extension *ex; ex = exts->exts[j]; - + for (n = 0; n < ex->used; n++) { - + fcgi_proc *proc; - unsigned long sum_load = 0; fcgi_extension_host *host; - + host = ex->hosts[n]; - + fcgi_restart_dead_procs(srv, p, host); - - for (proc = host->first; proc; proc = proc->next) { - sum_load += proc->load; - } - - if (host->num_procs && - host->num_procs < host->max_procs && - (sum_load / host->num_procs) > host->max_load_per_proc) { - /* overload, spawn new child */ - fcgi_proc *fp = NULL; - - if (p->conf.debug) { - log_error_write(srv, __FILE__, __LINE__, "s", - "overload detected, spawning a new child"); - } - - for (fp = host->unused_procs; fp && fp->pid != 0; fp = fp->next); - - if (fp) { - if (fp == host->unused_procs) host->unused_procs = fp->next; - - if (fp->next) fp->next->prev = NULL; - - host->max_id++; - } else { - fp = fastcgi_process_init(); - fp->id = host->max_id++; - } - - host->num_procs++; - - if (buffer_is_empty(host->unixsocket)) { - fp->port = host->port + fp->id; - } else { - buffer_copy_string_buffer(fp->socket, host->unixsocket); - buffer_append_string(fp->socket, "-"); - buffer_append_long(fp->socket, fp->id); - } - - if (fcgi_spawn_connection(srv, p, host, fp)) { - log_error_write(srv, __FILE__, __LINE__, "s", - "ERROR: spawning fcgi failed."); - return HANDLER_ERROR; - } - - fp->prev = NULL; - fp->next = host->first; - if (host->first) { - host->first->prev = fp; - } - host->first = fp; - } - - for (proc = host->first; proc; proc = proc->next) { - if (proc->load != 0) break; - if (host->num_procs <= host->min_procs) break; - if (proc->pid == 0) continue; - - if (srv->cur_ts - proc->last_used > host->idle_timeout) { - /* a proc is idling for a long time now, - * terminated it */ - - if (p->conf.debug) { - log_error_write(srv, __FILE__, __LINE__, "ssbsd", - "idle-timeout reached, terminating child:", - "socket:", proc->socket, - "pid", proc->pid); - } - - - if (proc->next) proc->next->prev = proc->prev; - if (proc->prev) proc->prev->next = proc->next; - - if (proc->prev == NULL) host->first = proc->next; - - proc->prev = NULL; - proc->next = host->unused_procs; - - if (host->unused_procs) host->unused_procs->prev = proc; - host->unused_procs = proc; - - kill(proc->pid, SIGTERM); - - proc->state = PROC_STATE_KILLED; - - log_error_write(srv, __FILE__, __LINE__, "ssbsd", - "killed:", - "socket:", proc->socket, - "pid", proc->pid); - - host->num_procs--; - - /* proc is now in unused, let the next second handle the next process */ - break; - } - } - + for (proc = host->unused_procs; proc; proc = proc->next) { int status; - + if (proc->pid == 0) continue; - + switch (waitpid(proc->pid, &status, WNOHANG)) { case 0: /* child still running after timeout, good */ @@ -3749,10 +3777,10 @@ TRIGGER_FUNC(mod_fastcgi_handle_trigger) { case -1: if (errno != EINTR) { /* no PID found ? should never happen */ - log_error_write(srv, __FILE__, __LINE__, "sddss", + log_error_write(srv, __FILE__, __LINE__, "sddss", "pid ", proc->pid, proc->state, "not found:", strerror(errno)); - + #if 0 if (errno == ECHILD) { /* someone else has cleaned up for us */ @@ -3766,22 +3794,23 @@ TRIGGER_FUNC(mod_fastcgi_handle_trigger) { /* the child should not terminate at all */ if (WIFEXITED(status)) { if (proc->state != PROC_STATE_KILLED) { - log_error_write(srv, __FILE__, __LINE__, "sdb", - "child exited:", - WEXITSTATUS(status), proc->socket); + log_error_write(srv, __FILE__, __LINE__, "sdb", + "child exited:", + WEXITSTATUS(status), proc->connection_name); } } else if (WIFSIGNALED(status)) { if (WTERMSIG(status) != SIGTERM) { - log_error_write(srv, __FILE__, __LINE__, "sd", - "child signaled:", + log_error_write(srv, __FILE__, __LINE__, "sd", + "child signaled:", WTERMSIG(status)); } } else { - log_error_write(srv, __FILE__, __LINE__, "sd", - "child died somehow:", + log_error_write(srv, __FILE__, __LINE__, "sd", + "child died somehow:", status); } proc->pid = 0; + if (proc->state == PROC_STATE_RUNNING) host->active_procs--; proc->state = PROC_STATE_UNSET; host->max_id--; } @@ -3794,6 +3823,7 @@ TRIGGER_FUNC(mod_fastcgi_handle_trigger) { } +int mod_fastcgi_plugin_init(plugin *p); int mod_fastcgi_plugin_init(plugin *p) { p->version = LIGHTTPD_VERSION_ID; p->name = buffer_init_string("fastcgi"); @@ -3808,8 +3838,8 @@ int mod_fastcgi_plugin_init(plugin *p) { p->handle_subrequest = mod_fastcgi_handle_subrequest; p->handle_joblist = mod_fastcgi_handle_joblist; p->handle_trigger = mod_fastcgi_handle_trigger; - + p->data = NULL; - + return 0; } diff --git a/src/mod_flv_streaming.c b/src/mod_flv_streaming.c new file mode 100644 index 0000000..7a100db --- /dev/null +++ b/src/mod_flv_streaming.c @@ -0,0 +1,277 @@ +#include "base.h" +#include "log.h" +#include "buffer.h" +#include "response.h" +#include "http_chunk.h" +#include "stat_cache.h" + +#include "plugin.h" + +#include <ctype.h> +#include <stdlib.h> +#include <string.h> + +/* plugin config for all request/connections */ + +typedef struct { + array *extensions; +} plugin_config; + +typedef struct { + PLUGIN_DATA; + + buffer *query_str; + array *get_params; + + plugin_config **config_storage; + + plugin_config conf; +} plugin_data; + +/* init the plugin data */ +INIT_FUNC(mod_flv_streaming_init) { + plugin_data *p; + + p = calloc(1, sizeof(*p)); + + p->query_str = buffer_init(); + p->get_params = array_init(); + + return p; +} + +/* detroy the plugin data */ +FREE_FUNC(mod_flv_streaming_free) { + plugin_data *p = p_d; + + UNUSED(srv); + + if (!p) return HANDLER_GO_ON; + + if (p->config_storage) { + size_t i; + + for (i = 0; i < srv->config_context->used; i++) { + plugin_config *s = p->config_storage[i]; + + if (!s) continue; + + array_free(s->extensions); + + free(s); + } + free(p->config_storage); + } + + buffer_free(p->query_str); + array_free(p->get_params); + + free(p); + + return HANDLER_GO_ON; +} + +/* handle plugin config and check values */ + +SETDEFAULTS_FUNC(mod_flv_streaming_set_defaults) { + plugin_data *p = p_d; + size_t i = 0; + + config_values_t cv[] = { + { "flv-streaming.extensions", NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION }, /* 0 */ + { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET } + }; + + if (!p) return HANDLER_ERROR; + + p->config_storage = calloc(1, srv->config_context->used * sizeof(specific_config *)); + + for (i = 0; i < srv->config_context->used; i++) { + plugin_config *s; + + s = calloc(1, sizeof(plugin_config)); + s->extensions = array_init(); + + cv[0].destination = s->extensions; + + p->config_storage[i] = s; + + if (0 != config_insert_values_global(srv, ((data_config *)srv->config_context->data[i])->value, cv)) { + return HANDLER_ERROR; + } + } + + return HANDLER_GO_ON; +} + +#define PATCH(x) \ + p->conf.x = s->x; +static int mod_flv_streaming_patch_connection(server *srv, connection *con, plugin_data *p) { + size_t i, j; + plugin_config *s = p->config_storage[0]; + + PATCH(extensions); + + /* skip the first, the global context */ + for (i = 1; i < srv->config_context->used; i++) { + data_config *dc = (data_config *)srv->config_context->data[i]; + s = p->config_storage[i]; + + /* condition didn't match */ + if (!config_check_cond(srv, con, dc)) continue; + + /* merge config */ + for (j = 0; j < dc->value->used; j++) { + data_unset *du = dc->value->data[j]; + + if (buffer_is_equal_string(du->key, CONST_STR_LEN("flv-streaming.extensions"))) { + PATCH(extensions); + } + } + } + + return 0; +} +#undef PATCH + +static int split_get_params(array *get_params, buffer *qrystr) { + size_t is_key = 1; + size_t i; + char *key = NULL, *val = NULL; + + key = qrystr->ptr; + + /* we need the \0 */ + for (i = 0; i < qrystr->used; i++) { + switch(qrystr->ptr[i]) { + case '=': + if (is_key) { + val = qrystr->ptr + i + 1; + + qrystr->ptr[i] = '\0'; + + is_key = 0; + } + + break; + case '&': + case '\0': /* fin symbol */ + if (!is_key) { + data_string *ds; + /* we need at least a = since the last & */ + + /* terminate the value */ + qrystr->ptr[i] = '\0'; + + if (NULL == (ds = (data_string *)array_get_unused_element(get_params, TYPE_STRING))) { + ds = data_string_init(); + } + buffer_copy_string_len(ds->key, key, strlen(key)); + buffer_copy_string_len(ds->value, val, strlen(val)); + + array_insert_unique(get_params, (data_unset *)ds); + } + + key = qrystr->ptr + i + 1; + val = NULL; + is_key = 1; + break; + } + } + + return 0; +} + +URIHANDLER_FUNC(mod_flv_streaming_path_handler) { + plugin_data *p = p_d; + int s_len; + size_t k; + + UNUSED(srv); + + if (con->mode != DIRECT) return HANDLER_GO_ON; + + if (buffer_is_empty(con->physical.path)) return HANDLER_GO_ON; + + mod_flv_streaming_patch_connection(srv, con, p); + + s_len = con->physical.path->used - 1; + + for (k = 0; k < p->conf.extensions->used; k++) { + data_string *ds = (data_string *)p->conf.extensions->data[k]; + int ct_len = ds->value->used - 1; + + if (ct_len > s_len) continue; + if (ds->value->used == 0) continue; + + if (0 == strncmp(con->physical.path->ptr + s_len - ct_len, ds->value->ptr, ct_len)) { + data_string *get_param; + stat_cache_entry *sce = NULL; + buffer *b; + int start; + char *err = NULL; + /* if there is a start=[0-9]+ in the header use it as start, + * otherwise send the full file */ + + array_reset(p->get_params); + buffer_copy_string_buffer(p->query_str, con->uri.query); + split_get_params(p->get_params, p->query_str); + + if (NULL == (get_param = (data_string *)array_get_element(p->get_params, "start"))) { + return HANDLER_GO_ON; + } + + /* too short */ + if (get_param->value->used < 2) return HANDLER_GO_ON; + + /* check if it is a number */ + start = strtol(get_param->value->ptr, &err, 10); + if (*err != '\0') { + return HANDLER_GO_ON; + } + + if (start <= 0) return HANDLER_GO_ON; + + /* check if start is > filesize */ + if (HANDLER_GO_ON != stat_cache_get_entry(srv, con, con->physical.path, &sce)) { + return HANDLER_GO_ON; + } + + if (start > sce->st.st_size) { + return HANDLER_GO_ON; + } + + /* we are safe now, let's build a flv header */ + b = chunkqueue_get_append_buffer(con->write_queue); + buffer_copy_string_len(b, CONST_STR_LEN("FLV\x1\x1\0\0\0\x9\0\0\0\x9")); + + http_chunk_append_file(srv, con, con->physical.path, start, sce->st.st_size - start); + + response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_STR_LEN("video/x-flv")); + + con->file_finished = 1; + + return HANDLER_FINISHED; + } + } + + /* not found */ + return HANDLER_GO_ON; +} + +/* this function is called at dlopen() time and inits the callbacks */ + +int mod_flv_streaming_plugin_init(plugin *p); +int mod_flv_streaming_plugin_init(plugin *p) { + p->version = LIGHTTPD_VERSION_ID; + p->name = buffer_init_string("flv_streaming"); + + p->init = mod_flv_streaming_init; + p->handle_physical = mod_flv_streaming_path_handler; + p->set_defaults = mod_flv_streaming_set_defaults; + p->cleanup = mod_flv_streaming_free; + + p->data = NULL; + + return 0; +} diff --git a/src/mod_indexfile.c b/src/mod_indexfile.c index 4a784c6..c01da2f 100644 --- a/src/mod_indexfile.c +++ b/src/mod_indexfile.c @@ -1,9 +1,3 @@ -#include <ctype.h> -#include <stdlib.h> -#include <string.h> -#include <errno.h> - - #include "base.h" #include "log.h" #include "buffer.h" @@ -12,6 +6,11 @@ #include "stat_cache.h" +#include <ctype.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> + /* plugin config for all request/connections */ typedef struct { @@ -20,51 +19,51 @@ typedef struct { typedef struct { PLUGIN_DATA; - + buffer *tmp_buf; - + plugin_config **config_storage; - - plugin_config conf; + + plugin_config conf; } plugin_data; /* init the plugin data */ INIT_FUNC(mod_indexfile_init) { plugin_data *p; - + p = calloc(1, sizeof(*p)); - + p->tmp_buf = buffer_init(); - + return p; } /* detroy the plugin data */ FREE_FUNC(mod_indexfile_free) { plugin_data *p = p_d; - + UNUSED(srv); if (!p) return HANDLER_GO_ON; - + if (p->config_storage) { size_t i; for (i = 0; i < srv->config_context->used; i++) { plugin_config *s = p->config_storage[i]; if (!s) continue; - + array_free(s->indexfiles); - + free(s); } free(p->config_storage); } - + buffer_free(p->tmp_buf); - + free(p); - + return HANDLER_GO_ON; } @@ -73,33 +72,33 @@ FREE_FUNC(mod_indexfile_free) { SETDEFAULTS_FUNC(mod_indexfile_set_defaults) { plugin_data *p = p_d; size_t i = 0; - - config_values_t cv[] = { + + config_values_t cv[] = { { "index-file.names", NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION }, /* 0 */ { "server.indexfiles", NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION }, /* 1 */ { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET } }; - + if (!p) return HANDLER_ERROR; - + p->config_storage = calloc(1, srv->config_context->used * sizeof(specific_config *)); - + for (i = 0; i < srv->config_context->used; i++) { plugin_config *s; - + s = calloc(1, sizeof(plugin_config)); s->indexfiles = array_init(); - + cv[0].destination = s->indexfiles; cv[1].destination = s->indexfiles; /* old name for [0] */ - + p->config_storage[i] = s; - + if (0 != config_insert_values_global(srv, ((data_config *)srv->config_context->data[i])->value, cv)) { return HANDLER_ERROR; } } - + return HANDLER_GO_ON; } @@ -108,21 +107,21 @@ SETDEFAULTS_FUNC(mod_indexfile_set_defaults) { static int mod_indexfile_patch_connection(server *srv, connection *con, plugin_data *p) { size_t i, j; plugin_config *s = p->config_storage[0]; - + PATCH(indexfiles); - + /* skip the first, the global context */ for (i = 1; i < srv->config_context->used; i++) { data_config *dc = (data_config *)srv->config_context->data[i]; s = p->config_storage[i]; - + /* condition didn't match */ if (!config_check_cond(srv, con, dc)) continue; - + /* merge config */ for (j = 0; j < dc->value->used; j++) { data_unset *du = dc->value->data[j]; - + if (buffer_is_equal_string(du->key, CONST_STR_LEN("server.indexfiles"))) { PATCH(indexfiles); } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("index-file.names"))) { @@ -130,7 +129,7 @@ static int mod_indexfile_patch_connection(server *srv, connection *con, plugin_d } } } - + return 0; } #undef PATCH @@ -139,81 +138,84 @@ URIHANDLER_FUNC(mod_indexfile_subrequest) { plugin_data *p = p_d; size_t k; stat_cache_entry *sce = NULL; - + + if (con->mode != DIRECT) return HANDLER_GO_ON; + if (con->uri.path->used == 0) return HANDLER_GO_ON; if (con->uri.path->ptr[con->uri.path->used - 2] != '/') return HANDLER_GO_ON; - + mod_indexfile_patch_connection(srv, con, p); - + if (con->conf.log_request_handling) { log_error_write(srv, __FILE__, __LINE__, "s", "-- handling the request as Indexfile"); log_error_write(srv, __FILE__, __LINE__, "sb", "URI :", con->uri.path); } - + /* indexfile */ for (k = 0; k < p->conf.indexfiles->used; k++) { data_string *ds = (data_string *)p->conf.indexfiles->data[k]; - + if (ds->value && ds->value->ptr[0] == '/') { - /* if the index-file starts with a prefix as use this file as + /* if the index-file starts with a prefix as use this file as * index-generator */ buffer_copy_string_buffer(p->tmp_buf, con->physical.doc_root); } else { buffer_copy_string_buffer(p->tmp_buf, con->physical.path); } buffer_append_string_buffer(p->tmp_buf, ds->value); - + if (HANDLER_ERROR == stat_cache_get_entry(srv, con, p->tmp_buf, &sce)) { if (errno == EACCES) { con->http_status = 403; buffer_reset(con->physical.path); - + return HANDLER_FINISHED; } - + if (errno != ENOENT && errno != ENOTDIR) { /* we have no idea what happend. let's tell the user so. */ - + con->http_status = 500; - + log_error_write(srv, __FILE__, __LINE__, "ssbsb", "file not found ... or so: ", strerror(errno), con->uri.path, "->", con->physical.path); - + buffer_reset(con->physical.path); - + return HANDLER_FINISHED; } continue; } - + /* rewrite uri.path to the real path (/ -> /index.php) */ buffer_append_string_buffer(con->uri.path, ds->value); buffer_copy_string_buffer(con->physical.path, p->tmp_buf); - + /* fce is already set up a few lines above */ - + return HANDLER_GO_ON; } - + /* not found */ return HANDLER_GO_ON; } /* this function is called at dlopen() time and inits the callbacks */ +int mod_indexfile_plugin_init(plugin *p); int mod_indexfile_plugin_init(plugin *p) { p->version = LIGHTTPD_VERSION_ID; p->name = buffer_init_string("indexfile"); - + p->init = mod_indexfile_init; p->handle_subrequest_start = mod_indexfile_subrequest; p->set_defaults = mod_indexfile_set_defaults; p->cleanup = mod_indexfile_free; - + p->data = NULL; - + return 0; } diff --git a/src/mod_magnet.c b/src/mod_magnet.c new file mode 100644 index 0000000..d10e3ff --- /dev/null +++ b/src/mod_magnet.c @@ -0,0 +1,1110 @@ +#include "base.h" +#include "log.h" +#include "buffer.h" + +#include "plugin.h" + +#include "mod_magnet_cache.h" +#include "response.h" +#include "stat_cache.h" +#include "status_counter.h" +#include "etag.h" + +#include <ctype.h> +#include <stdlib.h> +#include <string.h> +#include <assert.h> +#include <setjmp.h> + +#ifdef HAVE_LUA_H +#include <lua.h> +#include <lauxlib.h> + +#define MAGNET_CONFIG_RAW_URL "magnet.attract-raw-url-to" +#define MAGNET_CONFIG_PHYSICAL_PATH "magnet.attract-physical-path-to" +#define MAGNET_RESTART_REQUEST 99 + +/* plugin config for all request/connections */ + +static jmp_buf exceptionjmp; + +typedef struct { + array *url_raw; + array *physical_path; +} plugin_config; + +typedef struct { + PLUGIN_DATA; + + script_cache *cache; + + buffer *encode_buf; + + plugin_config **config_storage; + + plugin_config conf; +} plugin_data; + +/* init the plugin data */ +INIT_FUNC(mod_magnet_init) { + plugin_data *p; + + p = calloc(1, sizeof(*p)); + + p->cache = script_cache_init(); + p->encode_buf = buffer_init(); + + return p; +} + +/* detroy the plugin data */ +FREE_FUNC(mod_magnet_free) { + plugin_data *p = p_d; + + UNUSED(srv); + + if (!p) return HANDLER_GO_ON; + + if (p->config_storage) { + size_t i; + + for (i = 0; i < srv->config_context->used; i++) { + plugin_config *s = p->config_storage[i]; + + if (!s) continue; + + array_free(s->url_raw); + array_free(s->physical_path); + + free(s); + } + free(p->config_storage); + } + + script_cache_free(p->cache); + buffer_free(p->encode_buf); + + free(p); + + return HANDLER_GO_ON; +} + +/* handle plugin config and check values */ + +SETDEFAULTS_FUNC(mod_magnet_set_defaults) { + plugin_data *p = p_d; + size_t i = 0; + + config_values_t cv[] = { + { MAGNET_CONFIG_RAW_URL, NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION }, /* 0 */ + { MAGNET_CONFIG_PHYSICAL_PATH, NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION }, /* 1 */ + { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET } + }; + + if (!p) return HANDLER_ERROR; + + p->config_storage = calloc(1, srv->config_context->used * sizeof(specific_config *)); + + for (i = 0; i < srv->config_context->used; i++) { + plugin_config *s; + + s = calloc(1, sizeof(plugin_config)); + s->url_raw = array_init(); + s->physical_path = array_init(); + + cv[0].destination = s->url_raw; + cv[1].destination = s->physical_path; + + p->config_storage[i] = s; + + if (0 != config_insert_values_global(srv, ((data_config *)srv->config_context->data[i])->value, cv)) { + return HANDLER_ERROR; + } + } + + return HANDLER_GO_ON; +} + +#define PATCH(x) \ + p->conf.x = s->x; +static int mod_magnet_patch_connection(server *srv, connection *con, plugin_data *p) { + size_t i, j; + plugin_config *s = p->config_storage[0]; + + PATCH(url_raw); + PATCH(physical_path); + + /* skip the first, the global context */ + for (i = 1; i < srv->config_context->used; i++) { + data_config *dc = (data_config *)srv->config_context->data[i]; + s = p->config_storage[i]; + + /* condition didn't match */ + if (!config_check_cond(srv, con, dc)) continue; + + /* merge config */ + for (j = 0; j < dc->value->used; j++) { + data_unset *du = dc->value->data[j]; + + if (buffer_is_equal_string(du->key, CONST_STR_LEN(MAGNET_CONFIG_RAW_URL))) { + PATCH(url_raw); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN(MAGNET_CONFIG_PHYSICAL_PATH))) { + PATCH(physical_path); + } + } + } + + return 0; +} +#undef PATCH + +/* See http://lua-users.org/wiki/GeneralizedPairsAndIpairs for implementation details. */ + +/* Override the default pairs() function to allow us to use a __pairs metakey */ +static int magnet_pairs(lua_State *L) { + luaL_checkany(L, 1); + + if (luaL_getmetafield(L, 1, "__pairs")) { + lua_insert(L, 1); + lua_call(L, lua_gettop(L) - 1, LUA_MULTRET); + return lua_gettop(L); + } else { + lua_pushvalue(L, lua_upvalueindex(1)); + lua_insert(L, 1); + lua_call(L, lua_gettop(L) - 1, LUA_MULTRET); + return lua_gettop(L); + } +} + +/* Define a function that will iterate over an array* (in upval 1) using current position (upval 2) */ +static int magnet_array_next(lua_State *L) { + data_unset *du; + data_string *ds; + data_integer *di; + + size_t pos = lua_tointeger(L, lua_upvalueindex(1)); + array *a = lua_touserdata(L, lua_upvalueindex(2)); + + lua_settop(L, 0); + + if (pos >= a->used) return 0; + if (NULL != (du = a->data[pos])) { + if (du->key->used) { + lua_pushlstring(L, du->key->ptr, du->key->used - 1); + } + else { + lua_pushlstring(L, "", 0); + } + switch (du->type) { + case TYPE_STRING: + ds = (data_string *)du; + if (ds->value && ds->value->used) { + lua_pushlstring(L, ds->value->ptr, ds->value->used - 1); + } else { + lua_pushnil(L); + } + break; + case TYPE_COUNT: + case TYPE_INTEGER: + di = (data_integer *)du; + lua_pushinteger(L, di->value); + break; + default: + lua_pushnil(L); + break; + } + + /* Update our positional upval to reflect our new current position */ + pos++; + lua_pushinteger(L, pos); + lua_replace(L, lua_upvalueindex(1)); + + /* Returning 2 items on the stack (key, value) */ + return 2; + } + return 0; +} + +/* Create the closure necessary to iterate over the array *a with the above function */ +static int magnet_array_pairs(lua_State *L, array *a) { + lua_pushinteger(L, 0); /* Push our current pos (the start) into upval 1 */ + lua_pushlightuserdata(L, a); /* Push our array *a into upval 2 */ + lua_pushcclosure(L, magnet_array_next, 2); /* Push our new closure with 2 upvals */ + return 1; +} + +static int magnet_print(lua_State *L) { + const char *s = luaL_checkstring(L, 1); + server *srv; + + lua_pushstring(L, "lighty.srv"); + lua_gettable(L, LUA_REGISTRYINDEX); + srv = lua_touserdata(L, -1); + lua_pop(L, 1); + + log_error_write(srv, __FILE__, __LINE__, "ss", + "(lua-print)", s); + + return 0; +} + +static int magnet_stat(lua_State *L) { + const char *s = luaL_checkstring(L, 1); + server *srv; + connection *con; + buffer sb; + stat_cache_entry *sce = NULL; + + lua_pushstring(L, "lighty.srv"); + lua_gettable(L, LUA_REGISTRYINDEX); + srv = lua_touserdata(L, -1); + lua_pop(L, 1); + + lua_pushstring(L, "lighty.con"); + lua_gettable(L, LUA_REGISTRYINDEX); + con = lua_touserdata(L, -1); + lua_pop(L, 1); + + sb.ptr = (char *)s; + sb.used = sb.size = strlen(s) + 1; + + if (HANDLER_GO_ON != stat_cache_get_entry(srv, con, &sb, &sce)) { + lua_pushnil(L); + + return 1; + } + + lua_newtable(L); + + lua_pushboolean(L, S_ISREG(sce->st.st_mode)); + lua_setfield(L, -2, "is_file"); + + lua_pushboolean(L, S_ISDIR(sce->st.st_mode)); + lua_setfield(L, -2, "is_dir"); + + lua_pushboolean(L, S_ISCHR(sce->st.st_mode)); + lua_setfield(L, -2, "is_char"); + + lua_pushboolean(L, S_ISBLK(sce->st.st_mode)); + lua_setfield(L, -2, "is_block"); + + lua_pushboolean(L, S_ISSOCK(sce->st.st_mode)); + lua_setfield(L, -2, "is_socket"); + + lua_pushboolean(L, S_ISLNK(sce->st.st_mode)); + lua_setfield(L, -2, "is_link"); + + lua_pushboolean(L, S_ISFIFO(sce->st.st_mode)); + lua_setfield(L, -2, "is_fifo"); + + lua_pushinteger(L, sce->st.st_mtime); + lua_setfield(L, -2, "st_mtime"); + + lua_pushinteger(L, sce->st.st_ctime); + lua_setfield(L, -2, "st_ctime"); + + lua_pushinteger(L, sce->st.st_atime); + lua_setfield(L, -2, "st_atime"); + + lua_pushinteger(L, sce->st.st_uid); + lua_setfield(L, -2, "st_uid"); + + lua_pushinteger(L, sce->st.st_gid); + lua_setfield(L, -2, "st_gid"); + + lua_pushinteger(L, sce->st.st_size); + lua_setfield(L, -2, "st_size"); + + lua_pushinteger(L, sce->st.st_ino); + lua_setfield(L, -2, "st_ino"); + + + if (!buffer_is_empty(sce->etag)) { + /* we have to mutate the etag */ + buffer *b = buffer_init(); + etag_mutate(b, sce->etag); + + lua_pushlstring(L, b->ptr, b->used - 1); + buffer_free(b); + } else { + lua_pushnil(L); + } + lua_setfield(L, -2, "etag"); + + if (!buffer_is_empty(sce->content_type)) { + lua_pushlstring(L, sce->content_type->ptr, sce->content_type->used - 1); + } else { + lua_pushnil(L); + } + lua_setfield(L, -2, "content-type"); + + return 1; +} + + +static int magnet_atpanic(lua_State *L) { + const char *s = luaL_checkstring(L, 1); + server *srv; + + lua_pushstring(L, "lighty.srv"); + lua_gettable(L, LUA_REGISTRYINDEX); + srv = lua_touserdata(L, -1); + lua_pop(L, 1); + + log_error_write(srv, __FILE__, __LINE__, "ss", + "(lua-atpanic)", s); + + longjmp(exceptionjmp, 1); +} + +static int magnet_reqhdr_get(lua_State *L) { + connection *con; + data_string *ds; + + const char *key = luaL_checkstring(L, 2); + + lua_pushstring(L, "lighty.con"); + lua_gettable(L, LUA_REGISTRYINDEX); + con = lua_touserdata(L, -1); + lua_pop(L, 1); + + if (NULL != (ds = (data_string *)array_get_element(con->request.headers, key))) { + if (ds->value->used) { + lua_pushlstring(L, ds->value->ptr, ds->value->used - 1); + } else { + lua_pushnil(L); + } + } else { + lua_pushnil(L); + } + return 1; +} + +static int magnet_reqhdr_pairs(lua_State *L) { + connection *con; + + lua_pushstring(L, "lighty.con"); + lua_gettable(L, LUA_REGISTRYINDEX); + con = lua_touserdata(L, -1); + lua_pop(L, 1); + + return magnet_array_pairs(L, con->request.headers); +} + +static int magnet_status_get(lua_State *L) { + data_integer *di; + server *srv; + size_t key_len = 0; + + const char *key = luaL_checklstring(L, 2, &key_len); + + lua_pushstring(L, "lighty.srv"); + lua_gettable(L, LUA_REGISTRYINDEX); + srv = lua_touserdata(L, -1); + lua_pop(L, 1); + + di = status_counter_get_counter(srv, key, key_len); + + lua_pushnumber(L, (double)di->value); + + return 1; +} + +static int magnet_status_set(lua_State *L) { + size_t key_len = 0; + server *srv; + + const char *key = luaL_checklstring(L, 2, &key_len); + int counter = luaL_checkint(L, 3); + + lua_pushstring(L, "lighty.srv"); + lua_gettable(L, LUA_REGISTRYINDEX); + srv = lua_touserdata(L, -1); + lua_pop(L, 1); + + status_counter_set(srv, key, key_len, counter); + + return 0; +} + +static int magnet_status_pairs(lua_State *L) { + server *srv; + + lua_pushstring(L, "lighty.srv"); + lua_gettable(L, LUA_REGISTRYINDEX); + srv = lua_touserdata(L, -1); + lua_pop(L, 1); + + return magnet_array_pairs(L, srv->status); +} + +typedef struct { + const char *name; + enum { + MAGNET_ENV_UNSET, + + MAGNET_ENV_PHYICAL_PATH, + MAGNET_ENV_PHYICAL_REL_PATH, + MAGNET_ENV_PHYICAL_DOC_ROOT, + + MAGNET_ENV_URI_PATH, + MAGNET_ENV_URI_PATH_RAW, + MAGNET_ENV_URI_SCHEME, + MAGNET_ENV_URI_AUTHORITY, + MAGNET_ENV_URI_QUERY, + + MAGNET_ENV_REQUEST_METHOD, + MAGNET_ENV_REQUEST_URI, + MAGNET_ENV_REQUEST_ORIG_URI, + MAGNET_ENV_REQUEST_PATH_INFO, + MAGNET_ENV_REQUEST_REMOTE_IP, + MAGNET_ENV_REQUEST_PROTOCOL + } type; +} magnet_env_t; + +static const magnet_env_t magnet_env[] = { + { "physical.path", MAGNET_ENV_PHYICAL_PATH }, + { "physical.rel-path", MAGNET_ENV_PHYICAL_REL_PATH }, + { "physical.doc-root", MAGNET_ENV_PHYICAL_DOC_ROOT }, + + { "uri.path", MAGNET_ENV_URI_PATH }, + { "uri.path-raw", MAGNET_ENV_URI_PATH_RAW }, + { "uri.scheme", MAGNET_ENV_URI_SCHEME }, + { "uri.authority", MAGNET_ENV_URI_AUTHORITY }, + { "uri.query", MAGNET_ENV_URI_QUERY }, + + { "request.method", MAGNET_ENV_REQUEST_METHOD }, + { "request.uri", MAGNET_ENV_REQUEST_URI }, + { "request.orig-uri", MAGNET_ENV_REQUEST_ORIG_URI }, + { "request.path-info", MAGNET_ENV_REQUEST_PATH_INFO }, + { "request.remote-ip", MAGNET_ENV_REQUEST_REMOTE_IP }, + { "request.protocol", MAGNET_ENV_REQUEST_PROTOCOL }, + + { NULL, MAGNET_ENV_UNSET } +}; + +static buffer *magnet_env_get_buffer_by_id(server *srv, connection *con, int id) { + buffer *dest = NULL; + + UNUSED(srv); + + /** + * map all internal variables to lua + * + */ + + switch (id) { + case MAGNET_ENV_PHYICAL_PATH: dest = con->physical.path; break; + case MAGNET_ENV_PHYICAL_REL_PATH: dest = con->physical.rel_path; break; + case MAGNET_ENV_PHYICAL_DOC_ROOT: dest = con->physical.doc_root; break; + + case MAGNET_ENV_URI_PATH: dest = con->uri.path; break; + case MAGNET_ENV_URI_PATH_RAW: dest = con->uri.path_raw; break; + case MAGNET_ENV_URI_SCHEME: dest = con->uri.scheme; break; + case MAGNET_ENV_URI_AUTHORITY: dest = con->uri.authority; break; + case MAGNET_ENV_URI_QUERY: dest = con->uri.query; break; + + case MAGNET_ENV_REQUEST_METHOD: + buffer_copy_string(srv->tmp_buf, get_http_method_name(con->request.http_method)); + dest = srv->tmp_buf; + break; + case MAGNET_ENV_REQUEST_URI: dest = con->request.uri; break; + case MAGNET_ENV_REQUEST_ORIG_URI: dest = con->request.orig_uri; break; + case MAGNET_ENV_REQUEST_PATH_INFO: dest = con->request.pathinfo; break; + case MAGNET_ENV_REQUEST_REMOTE_IP: dest = con->dst_addr_buf; break; + case MAGNET_ENV_REQUEST_PROTOCOL: + buffer_copy_string(srv->tmp_buf, get_http_version_name(con->request.http_version)); + dest = srv->tmp_buf; + break; + + case MAGNET_ENV_UNSET: break; + } + + return dest; +} + +static buffer *magnet_env_get_buffer(server *srv, connection *con, const char *key) { + size_t i; + + for (i = 0; magnet_env[i].name; i++) { + if (0 == strcmp(key, magnet_env[i].name)) break; + } + + return magnet_env_get_buffer_by_id(srv, con, magnet_env[i].type); +} + +static int magnet_env_get(lua_State *L) { + server *srv; + connection *con; + + const char *key = luaL_checkstring(L, 2); + buffer *dest = NULL; + + lua_pushstring(L, "lighty.srv"); + lua_gettable(L, LUA_REGISTRYINDEX); + srv = lua_touserdata(L, -1); + lua_pop(L, 1); + + lua_pushstring(L, "lighty.con"); + lua_gettable(L, LUA_REGISTRYINDEX); + con = lua_touserdata(L, -1); + lua_pop(L, 1); + + dest = magnet_env_get_buffer(srv, con, key); + + if (dest && dest->used) { + lua_pushlstring(L, dest->ptr, dest->used - 1); + } else { + lua_pushnil(L); + } + + return 1; +} + +static int magnet_env_set(lua_State *L) { + server *srv; + connection *con; + + const char *key = luaL_checkstring(L, 2); + const char *val = luaL_checkstring(L, 3); + buffer *dest = NULL; + + lua_pushstring(L, "lighty.srv"); + lua_gettable(L, LUA_REGISTRYINDEX); + srv = lua_touserdata(L, -1); + lua_pop(L, 1); + + lua_pushstring(L, "lighty.con"); + lua_gettable(L, LUA_REGISTRYINDEX); + con = lua_touserdata(L, -1); + lua_pop(L, 1); + + if (NULL != (dest = magnet_env_get_buffer(srv, con, key))) { + buffer_copy_string(dest, val); + } else { + /* couldn't save */ + + return luaL_error(L, "couldn't store '%s' in lighty.env[]", key); + } + + return 0; +} + +static int magnet_env_next(lua_State *L) { + server *srv; + connection *con; + int pos = lua_tointeger(L, lua_upvalueindex(1)); + + buffer *dest; + + lua_pushstring(L, "lighty.srv"); + lua_gettable(L, LUA_REGISTRYINDEX); + srv = lua_touserdata(L, -1); + lua_pop(L, 1); + + lua_pushstring(L, "lighty.con"); + lua_gettable(L, LUA_REGISTRYINDEX); + con = lua_touserdata(L, -1); + lua_pop(L, 1); + + lua_settop(L, 0); + + if (NULL == magnet_env[pos].name) return 0; /* end of list */ + + lua_pushstring(L, magnet_env[pos].name); + + dest = magnet_env_get_buffer_by_id(srv, con, magnet_env[pos].type); + if (dest && dest->used) { + lua_pushlstring(L, dest->ptr, dest->used - 1); + } else { + lua_pushnil(L); + } + + /* Update our positional upval to reflect our new current position */ + pos++; + lua_pushinteger(L, pos); + lua_replace(L, lua_upvalueindex(1)); + + /* Returning 2 items on the stack (key, value) */ + return 2; +} + +static int magnet_env_pairs(lua_State *L) { + lua_pushinteger(L, 0); /* Push our current pos (the start) into upval 1 */ + lua_pushcclosure(L, magnet_env_next, 1); /* Push our new closure with 1 upvals */ + return 1; +} + +static int magnet_cgi_get(lua_State *L) { + connection *con; + data_string *ds; + + const char *key = luaL_checkstring(L, 2); + + lua_pushstring(L, "lighty.con"); + lua_gettable(L, LUA_REGISTRYINDEX); + con = lua_touserdata(L, -1); + lua_pop(L, 1); + + if (NULL != (ds = (data_string *)array_get_element(con->environment, key)) && ds->value->used) + lua_pushlstring(L, CONST_BUF_LEN(ds->value)); + else + lua_pushnil(L); + + return 1; +} + +static int magnet_cgi_set(lua_State *L) { + connection *con; + + const char *key = luaL_checkstring(L, 2); + const char *val = luaL_checkstring(L, 3); + + lua_pushstring(L, "lighty.con"); + lua_gettable(L, LUA_REGISTRYINDEX); + con = lua_touserdata(L, -1); + lua_pop(L, 1); + + array_set_key_value(con->environment, key, strlen(key), val, strlen(val)); + + return 0; +} + +static int magnet_cgi_pairs(lua_State *L) { + connection *con; + + lua_pushstring(L, "lighty.con"); + lua_gettable(L, LUA_REGISTRYINDEX); + con = lua_touserdata(L, -1); + lua_pop(L, 1); + + return magnet_array_pairs(L, con->environment); +} + + +static int magnet_copy_response_header(server *srv, connection *con, plugin_data *p, lua_State *L) { + UNUSED(p); + /** + * get the environment of the function + */ + + lua_getfenv(L, -1); /* -1 is the function */ + + /* lighty.header */ + + lua_getfield(L, -1, "lighty"); /* lighty.* from the env */ + assert(lua_istable(L, -1)); + + lua_getfield(L, -1, "header"); /* lighty.header */ + if (lua_istable(L, -1)) { + /* header is found, and is a table */ + + lua_pushnil(L); + while (lua_next(L, -2) != 0) { + if (lua_isstring(L, -1) && lua_isstring(L, -2)) { + const char *key, *val; + size_t key_len, val_len; + + key = lua_tolstring(L, -2, &key_len); + val = lua_tolstring(L, -1, &val_len); + + response_header_overwrite(srv, con, key, key_len, val, val_len); + } + + lua_pop(L, 1); + } + } + + lua_pop(L, 1); /* pop the header-table */ + lua_pop(L, 1); /* pop the lighty-env */ + lua_pop(L, 1); /* pop the function env */ + + return 0; +} + +/** + * walk through the content array + * + * content = { "<pre>", { file = "/content" } , "</pre>" } + * + * header["Content-Type"] = "text/html" + * + * return 200 + */ +static int magnet_attach_content(server *srv, connection *con, plugin_data *p, lua_State *L) { + UNUSED(p); + /** + * get the environment of the function + */ + + assert(lua_isfunction(L, -1)); + lua_getfenv(L, -1); /* -1 is the function */ + + lua_getfield(L, -1, "lighty"); /* lighty.* from the env */ + assert(lua_istable(L, -1)); + + lua_getfield(L, -1, "content"); /* lighty.content */ + if (lua_istable(L, -1)) { + int i; + /* header is found, and is a table */ + + for (i = 1; ; i++) { + lua_rawgeti(L, -1, i); + + /* -1 is the value and should be the value ... aka a table */ + if (lua_isstring(L, -1)) { + size_t s_len = 0; + const char *s = lua_tolstring(L, -1, &s_len); + + chunkqueue_append_mem(con->write_queue, s, s_len + 1); + } else if (lua_istable(L, -1)) { + lua_getfield(L, -1, "filename"); + lua_getfield(L, -2, "length"); + lua_getfield(L, -3, "offset"); + + if (lua_isstring(L, -3)) { /* filename has to be a string */ + buffer *fn = buffer_init(); + stat_cache_entry *sce; + + buffer_copy_string(fn, lua_tostring(L, -3)); + + if (HANDLER_GO_ON == stat_cache_get_entry(srv, con, fn, &sce)) { + off_t off = 0; + off_t len = 0; + + if (lua_isnumber(L, -1)) { + off = lua_tonumber(L, -1); + } + + if (lua_isnumber(L, -2)) { + len = lua_tonumber(L, -2); + } else { + len = sce->st.st_size; + } + + if (off < 0) { + return luaL_error(L, "offset for '%s' is negative", fn->ptr); + } + + if (len < off) { + return luaL_error(L, "offset > length for '%s'", fn->ptr); + } + + chunkqueue_append_file(con->write_queue, fn, off, len - off); + } + + buffer_free(fn); + } else { + lua_pop(L, 3 + 2); /* correct the stack */ + + return luaL_error(L, "content[%d] is a table and requires the field \"filename\"", i); + } + + lua_pop(L, 3); + } else if (lua_isnil(L, -1)) { + /* oops, end of list */ + + lua_pop(L, 1); + + break; + } else { + lua_pop(L, 4); + + return luaL_error(L, "content[%d] is neither a string nor a table: ", i); + } + + lua_pop(L, 1); /* pop the content[...] table */ + } + } else { + return luaL_error(L, "lighty.content has to be a table"); + } + lua_pop(L, 1); /* pop the header-table */ + lua_pop(L, 1); /* pop the lighty-table */ + lua_pop(L, 1); /* php the function env */ + + return 0; +} + +static int traceback (lua_State *L) { + if (!lua_isstring(L, 1)) /* 'message' not a string? */ + return 1; /* keep it intact */ + lua_getfield(L, LUA_GLOBALSINDEX, "debug"); + if (!lua_istable(L, -1)) { + lua_pop(L, 1); + return 1; + } + lua_getfield(L, -1, "traceback"); + if (!lua_isfunction(L, -1)) { + lua_pop(L, 2); + return 1; + } + lua_pushvalue(L, 1); /* pass error message */ + lua_pushinteger(L, 2); /* skip this function and traceback */ + lua_call(L, 2, 1); /* call debug.traceback */ + return 1; +} + +static int push_traceback(lua_State *L, int narg) { + int base = lua_gettop(L) - narg; /* function index */ + lua_pushcfunction(L, traceback); + lua_insert(L, base); + return base; +} + +static handler_t magnet_attract(server *srv, connection *con, plugin_data *p, buffer *name) { + lua_State *L; + int lua_return_value = -1; + int errfunc; + /* get the script-context */ + + + L = script_cache_get_script(srv, con, p->cache, name); + + if (lua_isstring(L, -1)) { + log_error_write(srv, __FILE__, __LINE__, + "sbss", + "loading script", + name, + "failed:", + lua_tostring(L, -1)); + + lua_pop(L, 1); + + assert(lua_gettop(L) == 0); /* only the function should be on the stack */ + + con->http_status = 500; + con->mode = DIRECT; + + return HANDLER_FINISHED; + } + + lua_pushstring(L, "lighty.srv"); + lua_pushlightuserdata(L, srv); + lua_settable(L, LUA_REGISTRYINDEX); /* registery[<id>] = srv */ + + lua_pushstring(L, "lighty.con"); + lua_pushlightuserdata(L, con); + lua_settable(L, LUA_REGISTRYINDEX); /* registery[<id>] = con */ + + lua_atpanic(L, magnet_atpanic); + + /** + * we want to create empty environment for our script + * + * setmetatable({}, {__index = _G}) + * + * if a function, symbol is not defined in our env, __index will lookup + * in the global env. + * + * all variables created in the script-env will be thrown + * away at the end of the script run. + */ + lua_newtable(L); /* my empty environment aka {} (sp += 1) */ + + /* we have to overwrite the print function */ + lua_pushcfunction(L, magnet_print); /* (sp += 1) */ + lua_setfield(L, -2, "print"); /* -1 is the env we want to set(sp -= 1) */ + + /** + * lighty.request[] has the HTTP-request headers + * lighty.content[] is a table of string/file + * lighty.header[] is a array to set response headers + */ + + lua_newtable(L); /* lighty.* (sp += 1) */ + + lua_newtable(L); /* {} (sp += 1) */ + lua_newtable(L); /* the meta-table for the request-table (sp += 1) */ + lua_pushcfunction(L, magnet_reqhdr_get); /* (sp += 1) */ + lua_setfield(L, -2, "__index"); /* (sp -= 1) */ + lua_pushcfunction(L, magnet_reqhdr_pairs); /* (sp += 1) */ + lua_setfield(L, -2, "__pairs"); /* (sp -= 1) */ + lua_setmetatable(L, -2); /* tie the metatable to request (sp -= 1) */ + lua_setfield(L, -2, "request"); /* content = {} (sp -= 1) */ + + lua_newtable(L); /* {} (sp += 1) */ + lua_newtable(L); /* the meta-table for the request-table (sp += 1) */ + lua_pushcfunction(L, magnet_env_get); /* (sp += 1) */ + lua_setfield(L, -2, "__index"); /* (sp -= 1) */ + lua_pushcfunction(L, magnet_env_set); /* (sp += 1) */ + lua_setfield(L, -2, "__newindex"); /* (sp -= 1) */ + lua_pushcfunction(L, magnet_env_pairs); /* (sp += 1) */ + lua_setfield(L, -2, "__pairs"); /* (sp -= 1) */ + lua_setmetatable(L, -2); /* tie the metatable to request (sp -= 1) */ + lua_setfield(L, -2, "env"); /* content = {} (sp -= 1) */ + + lua_newtable(L); /* {} (sp += 1) */ + lua_newtable(L); /* the meta-table for the request-table (sp += 1) */ + lua_pushcfunction(L, magnet_cgi_get); /* (sp += 1) */ + lua_setfield(L, -2, "__index"); /* (sp -= 1) */ + lua_pushcfunction(L, magnet_cgi_set); /* (sp += 1) */ + lua_setfield(L, -2, "__newindex"); /* (sp -= 1) */ + lua_pushcfunction(L, magnet_cgi_pairs); /* (sp += 1) */ + lua_setfield(L, -2, "__pairs"); /* (sp -= 1) */ + lua_setmetatable(L, -2); /* tie the metatable to req_env (sp -= 1) */ + lua_setfield(L, -2, "req_env"); /* content = {} (sp -= 1) */ + + lua_newtable(L); /* {} (sp += 1) */ + lua_newtable(L); /* the meta-table for the request-table (sp += 1) */ + lua_pushcfunction(L, magnet_status_get); /* (sp += 1) */ + lua_setfield(L, -2, "__index"); /* (sp -= 1) */ + lua_pushcfunction(L, magnet_status_set); /* (sp += 1) */ + lua_setfield(L, -2, "__newindex"); /* (sp -= 1) */ + lua_pushcfunction(L, magnet_status_pairs); /* (sp += 1) */ + lua_setfield(L, -2, "__pairs"); /* (sp -= 1) */ + lua_setmetatable(L, -2); /* tie the metatable to request (sp -= 1) */ + lua_setfield(L, -2, "status"); /* content = {} (sp -= 1) */ + + /* add empty 'content' and 'header' tables */ + lua_newtable(L); /* {} (sp += 1) */ + lua_setfield(L, -2, "content"); /* content = {} (sp -= 1) */ + + lua_newtable(L); /* {} (sp += 1) */ + lua_setfield(L, -2, "header"); /* header = {} (sp -= 1) */ + + lua_pushinteger(L, MAGNET_RESTART_REQUEST); + lua_setfield(L, -2, "RESTART_REQUEST"); + + lua_pushcfunction(L, magnet_stat); /* (sp += 1) */ + lua_setfield(L, -2, "stat"); /* -1 is the env we want to set (sp -= 1) */ + + lua_setfield(L, -2, "lighty"); /* lighty.* (sp -= 1) */ + + /* override the default pairs() function to our __pairs capable version */ + lua_getglobal(L, "pairs"); /* push original pairs() (sp += 1) */ + lua_pushcclosure(L, magnet_pairs, 1); + lua_setfield(L, -2, "pairs"); /* (sp -= 1) */ + + lua_newtable(L); /* the meta-table for the new env (sp += 1) */ + lua_pushvalue(L, LUA_GLOBALSINDEX); /* (sp += 1) */ + lua_setfield(L, -2, "__index"); /* { __index = _G } (sp -= 1) */ + lua_setmetatable(L, -2); /* setmetatable({}, {__index = _G}) (sp -= 1) */ + + + lua_setfenv(L, -2); /* on the stack should be a modified env (sp -= 1) */ + + errfunc = push_traceback(L, 0); + if (lua_pcall(L, 0, 1, errfunc)) { + lua_remove(L, errfunc); + log_error_write(srv, __FILE__, __LINE__, + "ss", + "lua_pcall():", + lua_tostring(L, -1)); + lua_pop(L, 1); /* remove the error-msg and the function copy from the stack */ + + assert(lua_gettop(L) == 1); /* only the function should be on the stack */ + + con->http_status = 500; + con->mode = DIRECT; + + return HANDLER_FINISHED; + } + lua_remove(L, errfunc); + + /* we should have the function-copy and the return value on the stack */ + assert(lua_gettop(L) == 2); + + if (lua_isnumber(L, -1)) { + /* if the ret-value is a number, take it */ + lua_return_value = (int)lua_tonumber(L, -1); + } + lua_pop(L, 1); /* pop the ret-value */ + + magnet_copy_response_header(srv, con, p, L); + + if (lua_return_value > 99) { + con->http_status = lua_return_value; + con->file_finished = 1; + + /* try { ...*/ + if (0 == setjmp(exceptionjmp)) { + magnet_attach_content(srv, con, p, L); + if (!chunkqueue_is_empty(con->write_queue)) { + con->mode = p->id; + } + } else { + /* } catch () { */ + con->http_status = 500; + con->mode = DIRECT; + } + + assert(lua_gettop(L) == 1); /* only the function should be on the stack */ + + /* we are finished */ + return HANDLER_FINISHED; + } else if (MAGNET_RESTART_REQUEST == lua_return_value) { + assert(lua_gettop(L) == 1); /* only the function should be on the stack */ + + return HANDLER_COMEBACK; + } else { + assert(lua_gettop(L) == 1); /* only the function should be on the stack */ + + return HANDLER_GO_ON; + } +} + +static handler_t magnet_attract_array(server *srv, connection *con, plugin_data *p, array *files) { + size_t i; + + /* no filename set */ + if (files->used == 0) return HANDLER_GO_ON; + + /** + * execute all files and jump out on the first !HANDLER_GO_ON + */ + for (i = 0; i < files->used; i++) { + data_string *ds = (data_string *)files->data[i]; + handler_t ret; + + if (buffer_is_empty(ds->value)) continue; + + ret = magnet_attract(srv, con, p, ds->value); + + if (ret != HANDLER_GO_ON) return ret; + } + + return HANDLER_GO_ON; +} + +URIHANDLER_FUNC(mod_magnet_uri_handler) { + plugin_data *p = p_d; + + mod_magnet_patch_connection(srv, con, p); + + return magnet_attract_array(srv, con, p, p->conf.url_raw); +} + +URIHANDLER_FUNC(mod_magnet_physical) { + plugin_data *p = p_d; + + mod_magnet_patch_connection(srv, con, p); + + return magnet_attract_array(srv, con, p, p->conf.physical_path); +} + + +/* this function is called at dlopen() time and inits the callbacks */ + +int mod_magnet_plugin_init(plugin *p); +int mod_magnet_plugin_init(plugin *p) { + p->version = LIGHTTPD_VERSION_ID; + p->name = buffer_init_string("magnet"); + + p->init = mod_magnet_init; + p->handle_uri_clean = mod_magnet_uri_handler; + p->handle_physical = mod_magnet_physical; + p->set_defaults = mod_magnet_set_defaults; + p->cleanup = mod_magnet_free; + + p->data = NULL; + + return 0; +} + +#else +int mod_magnet_plugin_init(plugin *p); +int mod_magnet_plugin_init(plugin *p) { + UNUSED(p); + return -1; +} +#endif diff --git a/src/mod_magnet_cache.c b/src/mod_magnet_cache.c new file mode 100644 index 0000000..50326cf --- /dev/null +++ b/src/mod_magnet_cache.c @@ -0,0 +1,137 @@ +#include "mod_magnet_cache.h" +#include "stat_cache.h" + +#include <stdlib.h> +#include <time.h> +#include <assert.h> + +#ifdef HAVE_LUA_H +#include <lualib.h> +#include <lauxlib.h> + +static script *script_init() { + script *sc; + + sc = calloc(1, sizeof(*sc)); + sc->name = buffer_init(); + sc->etag = buffer_init(); + + return sc; +} + +static void script_free(script *sc) { + if (!sc) return; + + lua_pop(sc->L, 1); /* the function copy */ + + buffer_free(sc->name); + buffer_free(sc->etag); + + lua_close(sc->L); + + free(sc); +} + +script_cache *script_cache_init() { + script_cache *p; + + p = calloc(1, sizeof(*p)); + + return p; +} + +void script_cache_free(script_cache *p) { + size_t i; + + if (!p) return; + + for (i = 0; i < p->used; i++) { + script_free(p->ptr[i]); + } + + free(p->ptr); + + free(p); +} + +lua_State *script_cache_get_script(server *srv, connection *con, script_cache *cache, buffer *name) { + size_t i; + script *sc = NULL; + stat_cache_entry *sce; + + for (i = 0; i < cache->used; i++) { + sc = cache->ptr[i]; + + if (buffer_is_equal(name, sc->name)) { + sc->last_used = time(NULL); + + /* oops, the script failed last time */ + + if (lua_gettop(sc->L) == 0) break; + + if (HANDLER_ERROR == stat_cache_get_entry(srv, con, sc->name, &sce)) { + lua_pop(sc->L, 1); /* pop the old function */ + break; + } + + if (!buffer_is_equal(sce->etag, sc->etag)) { + /* the etag is outdated, reload the function */ + lua_pop(sc->L, 1); + break; + } + + assert(lua_isfunction(sc->L, -1)); + lua_pushvalue(sc->L, -1); /* copy the function-reference */ + + return sc->L; + } + + sc = NULL; + } + + /* if the script was script already loaded but either got changed or + * failed to load last time */ + if (sc == NULL) { + sc = script_init(); + + if (cache->size == 0) { + cache->size = 16; + cache->ptr = malloc(cache->size * sizeof(*(cache->ptr))); + } else if (cache->used == cache->size) { + cache->size += 16; + cache->ptr = realloc(cache->ptr, cache->size * sizeof(*(cache->ptr))); + } + + cache->ptr[cache->used++] = sc; + + buffer_copy_string_buffer(sc->name, name); + + sc->L = luaL_newstate(); + luaL_openlibs(sc->L); + } + + sc->last_used = time(NULL); + + if (0 != luaL_loadfile(sc->L, name->ptr)) { + /* oops, an error, return it */ + + return sc->L; + } + + if (HANDLER_GO_ON == stat_cache_get_entry(srv, con, sc->name, &sce)) { + buffer_copy_string_buffer(sc->etag, sce->etag); + } + + /** + * pcall() needs the function on the stack + * + * as pcall() will pop the script from the stack when done, we have to + * duplicate it here + */ + assert(lua_isfunction(sc->L, -1)); + lua_pushvalue(sc->L, -1); /* copy the function-reference */ + + return sc->L; +} + +#endif diff --git a/src/mod_magnet_cache.h b/src/mod_magnet_cache.h new file mode 100644 index 0000000..50c9e44 --- /dev/null +++ b/src/mod_magnet_cache.h @@ -0,0 +1,33 @@ +#ifndef _MOD_MAGNET_CACHE_H_ +#define _MOD_MAGNET_CACHE_H_ + +#include "buffer.h" +#include "base.h" + +#ifdef HAVE_LUA_H +#include <lua.h> + +typedef struct { + buffer *name; + buffer *etag; + + lua_State *L; + + time_t last_used; /* LRU */ +} script; + +typedef struct { + script **ptr; + + size_t used; + size_t size; +} script_cache; + +script_cache *script_cache_init(void); +void script_cache_free(script_cache *cache); + +lua_State *script_cache_get_script(server *srv, connection *con, + script_cache *cache, buffer *name); + +#endif +#endif diff --git a/src/mod_mysql_vhost.c b/src/mod_mysql_vhost.c index bf13e09..df29186 100644 --- a/src/mod_mysql_vhost.c +++ b/src/mod_mysql_vhost.c @@ -21,12 +21,12 @@ #endif /* - * Plugin for lighttpd to use MySQL + * Plugin for lighttpd to use MySQL * for domain to directory lookups, * i.e virtual hosts (vhosts). - * - * Optionally sets fcgi_offset and fcgi_arg - * in preparation for fcgi.c to handle + * + * Optionally sets fcgi_offset and fcgi_arg + * in preparation for fcgi.c to handle * per-user fcgi chroot jails. * * /ada@riksnet.se 2004-12-06 @@ -35,15 +35,15 @@ #ifdef HAVE_MYSQL typedef struct { MYSQL *mysql; - + buffer *mydb; buffer *myuser; buffer *mypass; buffer *mysock; - + buffer *hostname; unsigned short port; - + buffer *mysql_pre; buffer *mysql_post; } plugin_config; @@ -51,12 +51,12 @@ typedef struct { /* global plugin data */ typedef struct { PLUGIN_DATA; - + buffer *tmp_buf; - + plugin_config **config_storage; - - plugin_config conf; + + plugin_config conf; } plugin_data; /* per connection plugin data */ @@ -70,7 +70,7 @@ typedef struct { /* init the plugin data */ INIT_FUNC(mod_mysql_vhost_init) { plugin_data *p; - + p = calloc(1, sizeof(*p)); p->tmp_buf = buffer_init(); @@ -83,35 +83,36 @@ SERVER_FUNC(mod_mysql_vhost_cleanup) { plugin_data *p = p_d; UNUSED(srv); - + #ifdef DEBUG - log_error_write(srv, __FILE__, __LINE__, "ss", + log_error_write(srv, __FILE__, __LINE__, "ss", "mod_mysql_vhost_cleanup", p ? "yes" : "NO"); #endif if (!p) return HANDLER_GO_ON; - + if (p->config_storage) { size_t i; for (i = 0; i < srv->config_context->used; i++) { plugin_config *s = p->config_storage[i]; if (!s) continue; - + mysql_close(s->mysql); - + buffer_free(s->mydb); buffer_free(s->myuser); buffer_free(s->mypass); buffer_free(s->mysock); buffer_free(s->mysql_pre); buffer_free(s->mysql_post); - + buffer_free(s->hostname); + free(s); } free(p->config_storage); } buffer_free(p->tmp_buf); - + free(p); return HANDLER_GO_ON; @@ -126,7 +127,7 @@ static void* mod_mysql_vhost_connection_data(server *srv, connection *con, void UNUSED(srv); #ifdef DEBUG - log_error_write(srv, __FILE__, __LINE__, "ss", + log_error_write(srv, __FILE__, __LINE__, "ss", "mod_mysql_connection_data", c ? "old" : "NEW"); #endif @@ -149,10 +150,10 @@ CONNECTION_FUNC(mod_mysql_vhost_handle_connection_close) { UNUSED(srv); #ifdef DEBUG - log_error_write(srv, __FILE__, __LINE__, "ss", + log_error_write(srv, __FILE__, __LINE__, "ss", "mod_mysql_vhost_handle_connection_close", c ? "yes" : "NO"); #endif - + if (!c) return HANDLER_GO_ON; buffer_free(c->server_name); @@ -183,14 +184,14 @@ SERVER_FUNC(mod_mysql_vhost_set_defaults) { { "mysql-vhost.port", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_SERVER }, { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET } }; - + p->config_storage = calloc(1, srv->config_context->used * sizeof(specific_config *)); - + for (i = 0; i < srv->config_context->used; i++) { plugin_config *s; buffer *sel; - - + + s = calloc(1, sizeof(plugin_config)); s->mydb = buffer_init(); s->myuser = buffer_init(); @@ -200,10 +201,10 @@ SERVER_FUNC(mod_mysql_vhost_set_defaults) { s->port = 0; /* default port for mysql */ sel = buffer_init(); s->mysql = NULL; - + s->mysql_pre = buffer_init(); s->mysql_post = buffer_init(); - + cv[0].destination = s->mydb; cv[1].destination = s->myuser; cv[2].destination = s->mypass; @@ -211,68 +212,87 @@ SERVER_FUNC(mod_mysql_vhost_set_defaults) { cv[4].destination = sel; cv[5].destination = s->hostname; cv[6].destination = &(s->port); - + p->config_storage[i] = s; - - if (config_insert_values_global(srv, + + if (config_insert_values_global(srv, ((data_config *)srv->config_context->data[i])->value, cv)) return HANDLER_ERROR; - + s->mysql_pre = buffer_init(); s->mysql_post = buffer_init(); - - if (sel->used && (qmark = index(sel->ptr, '?'))) { + + if (sel->used && (qmark = strchr(sel->ptr, '?'))) { *qmark = '\0'; buffer_copy_string(s->mysql_pre, sel->ptr); buffer_copy_string(s->mysql_post, qmark+1); } else { buffer_copy_string_buffer(s->mysql_pre, sel); } - + /* required: * - username - * - database - * + * - database + * * optional: * - password, default: empty * - socket, default: mysql default * - hostname, if set overrides socket * - port, default: 3306 */ - + /* all have to be set */ if (!(buffer_is_empty(s->myuser) || buffer_is_empty(s->mydb))) { + my_bool reconnect = 1; - int fd; - if (NULL == (s->mysql = mysql_init(NULL))) { log_error_write(srv, __FILE__, __LINE__, "s", "mysql_init() failed, exiting..."); - + return HANDLER_ERROR; } + +#if MYSQL_VERSION_ID >= 50013 + /* in mysql versions above 5.0.3 the reconnect flag is off by default */ + mysql_options(s->mysql, MYSQL_OPT_RECONNECT, &reconnect); +#endif + #define FOO(x) (s->x->used ? s->x->ptr : NULL) - - if (!mysql_real_connect(s->mysql, FOO(hostname), FOO(myuser), FOO(mypass), + +#if MYSQL_VERSION_ID >= 40100 + /* CLIENT_MULTI_STATEMENTS first appeared in 4.1 */ + if (!mysql_real_connect(s->mysql, FOO(hostname), FOO(myuser), FOO(mypass), + FOO(mydb), s->port, FOO(mysock), CLIENT_MULTI_STATEMENTS)) { +#else + if (!mysql_real_connect(s->mysql, FOO(hostname), FOO(myuser), FOO(mypass), FOO(mydb), s->port, FOO(mysock), 0)) { +#endif log_error_write(srv, __FILE__, __LINE__, "s", mysql_error(s->mysql)); - + return HANDLER_ERROR; } #undef FOO + +#if 0 /* set close_on_exec for mysql the hard way */ /* Note: this only works as it is done during startup, */ /* otherwise we cannot be sure that mysql is fd i-1 */ - if (-1 == (fd = open("/dev/null", 0))) { + { int fd; + if (-1 != (fd = open("/dev/null", 0))) { close(fd); - fcntl(fd-1, F_SETFD, FD_CLOEXEC); - } +#ifdef FD_CLOEXEC + fcntl(fd-1, F_SETFD, FD_CLOEXEC); +#endif + } } +#else +#ifdef FD_CLOEXEC + fcntl(s->mysql->net.fd, F_SETFD, FD_CLOEXEC); +#endif +#endif } } - - - return HANDLER_GO_ON; + return HANDLER_GO_ON; } #define PATCH(x) \ @@ -280,36 +300,36 @@ SERVER_FUNC(mod_mysql_vhost_set_defaults) { static int mod_mysql_vhost_patch_connection(server *srv, connection *con, plugin_data *p) { size_t i, j; plugin_config *s = p->config_storage[0]; - + PATCH(mysql_pre); PATCH(mysql_post); #ifdef HAVE_MYSQL PATCH(mysql); #endif - + /* skip the first, the global context */ for (i = 1; i < srv->config_context->used; i++) { data_config *dc = (data_config *)srv->config_context->data[i]; s = p->config_storage[i]; - + /* condition didn't match */ if (!config_check_cond(srv, con, dc)) continue; - + /* merge config */ for (j = 0; j < dc->value->used; j++) { data_unset *du = dc->value->data[j]; - + if (buffer_is_equal_string(du->key, CONST_STR_LEN("mysql-vhost.sql"))) { PATCH(mysql_pre); PATCH(mysql_post); } } - + if (s->mysql) { PATCH(mysql); } } - + return 0; } #undef PATCH @@ -355,6 +375,9 @@ CONNECTION_FUNC(mod_mysql_vhost_handle_docroot) { if (!row || cols < 1) { /* no such virtual host */ mysql_free_result(result); +#if MYSQL_VERSION_ID >= 40100 + while (mysql_next_result(p->conf.mysql) == 0); +#endif return HANDLER_GO_ON; } @@ -378,7 +401,7 @@ CONNECTION_FUNC(mod_mysql_vhost_handle_docroot) { /* fcgi_offset and fcgi_arg are optional */ if (cols > 1 && row[1]) { c->fcgi_offset = atoi(row[1]); - + if (cols > 2 && row[2]) { buffer_copy_string(c->fcgi_arg, row[2]); } else { @@ -388,43 +411,52 @@ CONNECTION_FUNC(mod_mysql_vhost_handle_docroot) { c->fcgi_offset = c->fcgi_arg->used = 0; } mysql_free_result(result); +#if MYSQL_VERSION_ID >= 40100 + while (mysql_next_result(p->conf.mysql) == 0); +#endif /* fix virtual server and docroot */ GO_ON: buffer_copy_string_buffer(con->server_name, c->server_name); buffer_copy_string_buffer(con->physical.doc_root, c->document_root); #ifdef DEBUG - log_error_write(srv, __FILE__, __LINE__, "sbbdb", - result ? "NOT CACHED" : "cached", + log_error_write(srv, __FILE__, __LINE__, "sbbdb", + result ? "NOT CACHED" : "cached", con->server_name, con->physical.doc_root, c->fcgi_offset, c->fcgi_arg); #endif - return HANDLER_GO_ON; + return HANDLER_GO_ON; ERR500: if (result) mysql_free_result(result); +#if MYSQL_VERSION_ID >= 40100 + while (mysql_next_result(p->conf.mysql) == 0); +#endif con->http_status = 500; /* Internal Error */ + con->mode = DIRECT; return HANDLER_FINISHED; } /* this function is called at dlopen() time and inits the callbacks */ +int mod_mysql_vhost_plugin_init(plugin *p); int mod_mysql_vhost_plugin_init(plugin *p) { - p->version = LIGHTTPD_VERSION_ID; - p->name = buffer_init_string("mysql_vhost"); + p->version = LIGHTTPD_VERSION_ID; + p->name = buffer_init_string("mysql_vhost"); + + p->init = mod_mysql_vhost_init; + p->cleanup = mod_mysql_vhost_cleanup; + p->connection_reset = mod_mysql_vhost_handle_connection_close; - p->init = mod_mysql_vhost_init; - p->cleanup = mod_mysql_vhost_cleanup; - p->handle_request_done = mod_mysql_vhost_handle_connection_close; + p->set_defaults = mod_mysql_vhost_set_defaults; + p->handle_docroot = mod_mysql_vhost_handle_docroot; - p->set_defaults = mod_mysql_vhost_set_defaults; - p->handle_docroot = mod_mysql_vhost_handle_docroot; - return 0; } #else /* we don't have mysql support, this plugin does nothing */ +int mod_mysql_vhost_plugin_init(plugin *p); int mod_mysql_vhost_plugin_init(plugin *p) { p->version = LIGHTTPD_VERSION_ID; - p->name = buffer_init_string("mysql_vhost"); + p->name = buffer_init_string("mysql_vhost"); return 0; } diff --git a/src/mod_proxy.c b/src/mod_proxy.c index 6baf459..09d4fc1 100644 --- a/src/mod_proxy.c +++ b/src/mod_proxy.c @@ -1,13 +1,3 @@ -#include <sys/types.h> - -#include <unistd.h> -#include <errno.h> -#include <fcntl.h> -#include <string.h> -#include <stdlib.h> -#include <ctype.h> -#include <assert.h> - #include "buffer.h" #include "server.h" #include "keyvalue.h" @@ -24,6 +14,16 @@ #include "inet_ntop_cache.h" #include "crc32.h" +#include <sys/types.h> + +#include <unistd.h> +#include <errno.h> +#include <fcntl.h> +#include <string.h> +#include <stdlib.h> +#include <ctype.h> +#include <assert.h> + #include <stdio.h> #ifdef HAVE_SYS_FILIO_H @@ -38,16 +38,16 @@ #define PROXY_RETRY_TIMEOUT 60 /** - * - * the proxy module is based on the fastcgi module - * + * + * the proxy module is based on the fastcgi module + * * 28.06.2004 Jan Kneschke The first release * 01.07.2004 Evgeny Rodichev Several bugfixes and cleanups * - co-ordinate up- and downstream flows correctly (proxy_demux_response * and proxy_handle_fdevent) * - correctly transfer upstream http_response_status; * - some unused structures removed. - * + * * TODO: - delay upstream read if write_queue is too large * (to prevent memory eating, like in apache). Shoud be * configurable). @@ -63,29 +63,29 @@ typedef enum { typedef struct { array *extensions; - int debug; + unsigned short debug; proxy_balance_t balance; } plugin_config; typedef struct { PLUGIN_DATA; - + buffer *parse_response; buffer *balance_buf; - + plugin_config **config_storage; - + plugin_config conf; } plugin_data; -typedef enum { - PROXY_STATE_INIT, - PROXY_STATE_CONNECT, - PROXY_STATE_PREPARE_WRITE, - PROXY_STATE_WRITE, - PROXY_STATE_READ, - PROXY_STATE_ERROR +typedef enum { + PROXY_STATE_INIT, + PROXY_STATE_CONNECT, + PROXY_STATE_PREPARE_WRITE, + PROXY_STATE_WRITE, + PROXY_STATE_READ, + PROXY_STATE_ERROR } proxy_connection_state_t; enum { PROXY_STDOUT, PROXY_END_REQUEST }; @@ -93,36 +93,36 @@ enum { PROXY_STDOUT, PROXY_END_REQUEST }; typedef struct { proxy_connection_state_t state; time_t state_timestamp; - + data_proxy *host; - + buffer *response; buffer *response_header; chunkqueue *wb; - + int fd; /* fd to the proxy process */ int fde_ndx; /* index into the fd-event buffer */ size_t path_info_offset; /* start of path_info in uri.path */ - + connection *remote_conn; /* dump pointer */ plugin_data *plugin_data; /* dump pointer */ } handler_ctx; /* ok, we need a prototype */ -static handler_t proxy_handle_fdevent(void *s, void *ctx, int revents); +static handler_t proxy_handle_fdevent(server *srv, void *ctx, int revents); -static handler_ctx * handler_ctx_init() { +static handler_ctx * handler_ctx_init(void) { handler_ctx * hctx; - + hctx = calloc(1, sizeof(*hctx)); - + hctx->state = PROXY_STATE_INIT; hctx->host = NULL; - + hctx->response = buffer_init(); hctx->response_header = buffer_init(); @@ -130,7 +130,7 @@ static handler_ctx * handler_ctx_init() { hctx->fd = -1; hctx->fde_ndx = -1; - + return hctx; } @@ -138,47 +138,47 @@ static void handler_ctx_free(handler_ctx *hctx) { buffer_free(hctx->response); buffer_free(hctx->response_header); chunkqueue_free(hctx->wb); - + free(hctx); } INIT_FUNC(mod_proxy_init) { plugin_data *p; - + p = calloc(1, sizeof(*p)); - + p->parse_response = buffer_init(); p->balance_buf = buffer_init(); - + return p; } FREE_FUNC(mod_proxy_free) { plugin_data *p = p_d; - + UNUSED(srv); buffer_free(p->parse_response); buffer_free(p->balance_buf); - + if (p->config_storage) { size_t i; for (i = 0; i < srv->config_context->used; i++) { plugin_config *s = p->config_storage[i]; - + if (s) { - + array_free(s->extensions); - + free(s); } } free(p->config_storage); } - + free(p); - + return HANDLER_GO_ON; } @@ -186,37 +186,37 @@ SETDEFAULTS_FUNC(mod_proxy_set_defaults) { plugin_data *p = p_d; data_unset *du; size_t i = 0; - - config_values_t cv[] = { + + config_values_t cv[] = { { "proxy.server", NULL, T_CONFIG_LOCAL, T_CONFIG_SCOPE_CONNECTION }, /* 0 */ { "proxy.debug", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 1 */ { "proxy.balance", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 2 */ { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET } }; - + p->config_storage = calloc(1, srv->config_context->used * sizeof(specific_config *)); - + for (i = 0; i < srv->config_context->used; i++) { plugin_config *s; array *ca; - + s = malloc(sizeof(plugin_config)); s->extensions = array_init(); s->debug = 0; - + cv[0].destination = s->extensions; cv[1].destination = &(s->debug); cv[2].destination = p->balance_buf; buffer_reset(p->balance_buf); - + p->config_storage[i] = s; ca = ((data_config *)srv->config_context->data[i])->value; - + if (0 != config_insert_values_global(srv, ca, cv)) { return HANDLER_ERROR; } - + if (buffer_is_empty(p->balance_buf)) { s->balance = PROXY_BALANCE_FAIR; } else if (buffer_is_equal_string(p->balance_buf, CONST_STR_LEN("fair"))) { @@ -226,7 +226,7 @@ SETDEFAULTS_FUNC(mod_proxy_set_defaults) { } else if (buffer_is_equal_string(p->balance_buf, CONST_STR_LEN("hash"))) { s->balance = PROXY_BALANCE_HASH; } else { - log_error_write(srv, __FILE__, __LINE__, "sb", + log_error_write(srv, __FILE__, __LINE__, "sb", "proxy.balance has to be one of: fair, round-robin, hash, but not:", p->balance_buf); return HANDLER_ERROR; } @@ -234,92 +234,91 @@ SETDEFAULTS_FUNC(mod_proxy_set_defaults) { if (NULL != (du = array_get_element(ca, "proxy.server"))) { size_t j; data_array *da = (data_array *)du; - + if (du->type != TYPE_ARRAY) { - log_error_write(srv, __FILE__, __LINE__, "sss", + log_error_write(srv, __FILE__, __LINE__, "sss", "unexpected type for key: ", "proxy.server", "array of strings"); - + return HANDLER_ERROR; } - - /* + + /* * proxy.server = ( "<ext>" => ..., * "<ext>" => ... ) */ - + for (j = 0; j < da->value->used; j++) { data_array *da_ext = (data_array *)da->value->data[j]; size_t n; - + if (da_ext->type != TYPE_ARRAY) { - log_error_write(srv, __FILE__, __LINE__, "sssbs", - "unexpected type for key: ", "proxy.server", + log_error_write(srv, __FILE__, __LINE__, "sssbs", + "unexpected type for key: ", "proxy.server", "[", da->value->data[j]->key, "](string)"); - + return HANDLER_ERROR; } - - /* - * proxy.server = ( "<ext>" => - * ( "<host>" => ( ... ), + + /* + * proxy.server = ( "<ext>" => + * ( "<host>" => ( ... ), * "<host>" => ( ... ) - * ), + * ), * "<ext>" => ... ) */ - + for (n = 0; n < da_ext->value->used; n++) { data_array *da_host = (data_array *)da_ext->value->data[n]; - + data_proxy *df; data_array *dfa; - - config_values_t pcv[] = { + + config_values_t pcv[] = { { "host", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 0 */ { "port", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 1 */ - { "balance", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 2 */ { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET } }; - + if (da_host->type != TYPE_ARRAY) { - log_error_write(srv, __FILE__, __LINE__, "ssSBS", - "unexpected type for key:", - "proxy.server", + log_error_write(srv, __FILE__, __LINE__, "ssSBS", + "unexpected type for key:", + "proxy.server", "[", da_ext->value->data[n]->key, "](string)"); - + return HANDLER_ERROR; } - + df = data_proxy_init(); - + df->port = 80; - + buffer_copy_string_buffer(df->key, da_host->key); - + pcv[0].destination = df->host; pcv[1].destination = &(df->port); - + if (0 != config_insert_values_internal(srv, da_host->value, pcv)) { return HANDLER_ERROR; } - + if (buffer_is_empty(df->host)) { - log_error_write(srv, __FILE__, __LINE__, "sbbbs", - "missing key (string):", + log_error_write(srv, __FILE__, __LINE__, "sbbbs", + "missing key (string):", da->key, da_ext->key, da_host->key, "host"); - + return HANDLER_ERROR; } - + /* if extension already exists, take it */ - + if (NULL == (dfa = (data_array *)array_get_element(s->extensions, da_ext->key->ptr))) { dfa = data_array_init(); - + buffer_copy_string_buffer(dfa->key, da_ext->key); - + array_insert_unique(dfa->value, (data_unset *)df); array_insert_unique(s->extensions, (data_unset *)dfa); } else { @@ -329,19 +328,19 @@ SETDEFAULTS_FUNC(mod_proxy_set_defaults) { } } } - + return HANDLER_GO_ON; } -void proxy_connection_close(server *srv, handler_ctx *hctx) { +static void proxy_connection_close(server *srv, handler_ctx *hctx) { plugin_data *p; connection *con; - + if (NULL == hctx) return; - + p = hctx->plugin_data; con = hctx->remote_conn; - + if (hctx->fd != -1) { fdevent_event_del(srv->ev, &(hctx->fde_ndx), hctx->fd); fdevent_unregister(srv->ev, hctx->fd); @@ -349,54 +348,73 @@ void proxy_connection_close(server *srv, handler_ctx *hctx) { close(hctx->fd); srv->cur_fds--; } - + + if (hctx->host) { + hctx->host->usage--; + } + handler_ctx_free(hctx); - con->plugin_ctx[p->id] = NULL; + con->plugin_ctx[p->id] = NULL; } static int proxy_establish_connection(server *srv, handler_ctx *hctx) { struct sockaddr *proxy_addr; struct sockaddr_in proxy_addr_in; +#if defined(HAVE_IPV6) && defined(HAVE_INET_PTON) + struct sockaddr_in6 proxy_addr_in6; +#endif socklen_t servlen; - + plugin_data *p = hctx->plugin_data; data_proxy *host= hctx->host; int proxy_fd = hctx->fd; - - memset(&proxy_addr, 0, sizeof(proxy_addr)); - - proxy_addr_in.sin_family = AF_INET; - proxy_addr_in.sin_addr.s_addr = inet_addr(host->host->ptr); - proxy_addr_in.sin_port = htons(host->port); - servlen = sizeof(proxy_addr_in); - - proxy_addr = (struct sockaddr *) &proxy_addr_in; - + + +#if defined(HAVE_IPV6) && defined(HAVE_INET_PTON) + if (strstr(host->host->ptr, ":")) { + memset(&proxy_addr_in6, 0, sizeof(proxy_addr_in6)); + proxy_addr_in6.sin6_family = AF_INET6; + inet_pton(AF_INET6, host->host->ptr, (char *) &proxy_addr_in6.sin6_addr); + proxy_addr_in6.sin6_port = htons(host->port); + servlen = sizeof(proxy_addr_in6); + proxy_addr = (struct sockaddr *) &proxy_addr_in6; + } else +#endif + { + memset(&proxy_addr_in, 0, sizeof(proxy_addr_in)); + proxy_addr_in.sin_family = AF_INET; + proxy_addr_in.sin_addr.s_addr = inet_addr(host->host->ptr); + proxy_addr_in.sin_port = htons(host->port); + servlen = sizeof(proxy_addr_in); + proxy_addr = (struct sockaddr *) &proxy_addr_in; + } + + if (-1 == connect(proxy_fd, proxy_addr, servlen)) { if (errno == EINPROGRESS || errno == EALREADY) { if (p->conf.debug) { - log_error_write(srv, __FILE__, __LINE__, "sd", + log_error_write(srv, __FILE__, __LINE__, "sd", "connect delayed:", proxy_fd); } - + return 1; } else { - - log_error_write(srv, __FILE__, __LINE__, "sdsd", + + log_error_write(srv, __FILE__, __LINE__, "sdsd", "connect failed:", proxy_fd, strerror(errno), errno); - + return -1; } } if (p->conf.debug) { - log_error_write(srv, __FILE__, __LINE__, "sd", + log_error_write(srv, __FILE__, __LINE__, "sd", "connect succeeded: ", proxy_fd); } return 0; } -void proxy_set_header(connection *con, const char *key, const char *value) { +static void proxy_set_header(connection *con, const char *key, const char *value) { data_string *ds_dst; if (NULL == (ds_dst = (data_string *)array_get_unused_element(con->request.headers, TYPE_STRING))) { @@ -408,7 +426,7 @@ void proxy_set_header(connection *con, const char *key, const char *value) { array_insert_unique(con->request.headers, (data_unset *)ds_dst); } -void proxy_append_header(connection *con, const char *key, const char *value) { +static void proxy_append_header(connection *con, const char *key, const char *value) { data_string *ds_dst; if (NULL == (ds_dst = (data_string *)array_get_unused_element(con->request.headers, TYPE_STRING))) { @@ -423,25 +441,25 @@ void proxy_append_header(connection *con, const char *key, const char *value) { static int proxy_create_env(server *srv, handler_ctx *hctx) { size_t i; - + connection *con = hctx->remote_conn; buffer *b; - + /* build header */ b = chunkqueue_get_append_buffer(hctx->wb); - + /* request line */ buffer_copy_string(b, get_http_method_name(con->request.http_method)); - BUFFER_APPEND_STRING_CONST(b, " "); - + buffer_append_string_len(b, CONST_STR_LEN(" ")); + buffer_append_string_buffer(b, con->request.uri); - BUFFER_APPEND_STRING_CONST(b, " HTTP/1.0\r\n"); + buffer_append_string_len(b, CONST_STR_LEN(" HTTP/1.0\r\n")); proxy_append_header(con, "X-Forwarded-For", (char *)inet_ntop_cache_get_ip(srv, &(con->dst_addr))); - /* http_host is NOT is just a pointer to a buffer + /* http_host is NOT is just a pointer to a buffer * which is NULL if it is not set */ - if (con->request.http_host && + if (con->request.http_host && !buffer_is_empty(con->request.http_host)) { proxy_set_header(con, "X-Host", con->request.http_host->ptr); } @@ -450,24 +468,25 @@ static int proxy_create_env(server *srv, handler_ctx *hctx) { /* request header */ for (i = 0; i < con->request.headers->used; i++) { data_string *ds; - + ds = (data_string *)con->request.headers->data[i]; - + if (ds->value->used && ds->key->used) { if (buffer_is_equal_string(ds->key, CONST_STR_LEN("Connection"))) continue; - + if (buffer_is_equal_string(ds->key, CONST_STR_LEN("Proxy-Connection"))) continue; + buffer_append_string_buffer(b, ds->key); - BUFFER_APPEND_STRING_CONST(b, ": "); + buffer_append_string_len(b, CONST_STR_LEN(": ")); buffer_append_string_buffer(b, ds->value); - BUFFER_APPEND_STRING_CONST(b, "\r\n"); + buffer_append_string_len(b, CONST_STR_LEN("\r\n")); } } - - BUFFER_APPEND_STRING_CONST(b, "\r\n"); - + + buffer_append_string_len(b, CONST_STR_LEN("\r\n")); + hctx->wb->bytes_in += b->used - 1; /* body */ - + if (con->request.content_length) { chunkqueue *req_cq = con->request_content_queue; chunk *req_c; @@ -480,7 +499,7 @@ static int proxy_create_env(server *srv, handler_ctx *hctx) { /* we announce toWrite octects * now take all the request_content chunk that we need to fill this request - * */ + * */ switch (req_c->type) { case FILE_CHUNK: @@ -508,26 +527,26 @@ static int proxy_create_env(server *srv, handler_ctx *hctx) { req_c->offset += weHave; req_cq->bytes_out += weHave; - + hctx->wb->bytes_in += weHave; break; default: break; } - + offset += weHave; } } - + return 0; } static int proxy_set_state(server *srv, handler_ctx *hctx, proxy_connection_state_t state) { hctx->state = state; hctx->state_timestamp = srv->cur_ts; - + return 0; } @@ -535,19 +554,19 @@ static int proxy_set_state(server *srv, handler_ctx *hctx, proxy_connection_stat static int proxy_response_parse(server *srv, connection *con, plugin_data *p, buffer *in) { char *s, *ns; int http_response_status = -1; - + UNUSED(srv); /* \r\n -> \0\0 */ - + buffer_copy_string_buffer(p->parse_response, in); - + for (s = p->parse_response->ptr; NULL != (ns = strstr(s, "\r\n")); s = ns + 2) { char *key, *value; int key_len; data_string *ds; int copy_header; - + ns[0] = '\0'; ns[1] = '\0'; @@ -567,7 +586,7 @@ static int proxy_response_parse(server *srv, connection *con, plugin_data *p, bu con->parsed_response |= HTTP_STATUS; continue; } - + if (NULL == (value = strchr(s, ':'))) { /* now we expect: "<key>: <value>\n" */ @@ -576,13 +595,13 @@ static int proxy_response_parse(server *srv, connection *con, plugin_data *p, bu key = s; key_len = value - key; - + value++; /* strip WS */ while (*value == ' ' || *value == '\t') value++; - + copy_header = 1; - + switch(key_len) { case 4: if (0 == strncasecmp(key, "Date", key_len)) { @@ -615,11 +634,11 @@ static int proxy_response_parse(server *srv, connection *con, plugin_data *p, bu } buffer_copy_string_len(ds->key, key, key_len); buffer_copy_string(ds->value, value); - + array_insert_unique(con->response.headers, (data_unset *)ds); } } - + return 0; } @@ -628,14 +647,14 @@ static int proxy_demux_response(server *srv, handler_ctx *hctx) { int fin = 0; int b; ssize_t r; - + plugin_data *p = hctx->plugin_data; connection *con = hctx->remote_conn; int proxy_fd = hctx->fd; - + /* check how much we have to read */ if (ioctl(hctx->fd, FIONREAD, &b)) { - log_error_write(srv, __FILE__, __LINE__, "sd", + log_error_write(srv, __FILE__, __LINE__, "sd", "ioctl failed: ", proxy_fd); return -1; @@ -653,24 +672,25 @@ static int proxy_demux_response(server *srv, handler_ctx *hctx) { buffer_prepare_append(hctx->response, b + 1); hctx->response->used = 1; } else { - buffer_prepare_append(hctx->response, hctx->response->used + b); + buffer_prepare_append(hctx->response, b); } - + if (-1 == (r = read(hctx->fd, hctx->response->ptr + hctx->response->used - 1, b))) { - log_error_write(srv, __FILE__, __LINE__, "sds", + if (errno == EAGAIN) return 0; + log_error_write(srv, __FILE__, __LINE__, "sds", "unexpected end-of-file (perhaps the proxy process died):", proxy_fd, strerror(errno)); return -1; } - + /* this should be catched by the b > 0 above */ assert(r); - + hctx->response->used += r; hctx->response->ptr[hctx->response->used - 1] = '\0'; #if 0 - log_error_write(srv, __FILE__, __LINE__, "sdsbs", + log_error_write(srv, __FILE__, __LINE__, "sdsbs", "demux: Response buffer len", hctx->response->used, ":", hctx->response, ":"); #endif @@ -678,171 +698,158 @@ static int proxy_demux_response(server *srv, handler_ctx *hctx) { con->got_response = 1; buffer_prepare_copy(hctx->response_header, 128); } - + if (0 == con->file_started) { char *c; - + /* search for the \r\n\r\n in the string */ if (NULL != (c = buffer_search_string_len(hctx->response, "\r\n\r\n", 4))) { size_t hlen = c - hctx->response->ptr + 4; size_t blen = hctx->response->used - hlen - 1; /* found */ - + buffer_append_string_len(hctx->response_header, hctx->response->ptr, c - hctx->response->ptr + 4); #if 0 log_error_write(srv, __FILE__, __LINE__, "sb", "Header:", hctx->response_header); #endif /* parse the response header */ proxy_response_parse(srv, con, p, hctx->response_header); - + /* enable chunked-transfer-encoding */ if (con->request.http_version == HTTP_VERSION_1_1 && !(con->parsed_response & HTTP_CONTENT_LENGTH)) { con->response.transfer_encoding = HTTP_TRANSFER_ENCODING_CHUNKED; } - + con->file_started = 1; if (blen) { http_chunk_append_mem(srv, con, c + 4, blen + 1); - joblist_append(srv, con); } hctx->response->used = 0; + joblist_append(srv, con); } } else { http_chunk_append_mem(srv, con, hctx->response->ptr, hctx->response->used); joblist_append(srv, con); hctx->response->used = 0; } - + } else { /* reading from upstream done */ con->file_finished = 1; - + http_chunk_append_mem(srv, con, NULL, 0); joblist_append(srv, con); - + fin = 1; } - + return fin; } static handler_t proxy_write_request(server *srv, handler_ctx *hctx) { data_proxy *host= hctx->host; - plugin_data *p = hctx->plugin_data; connection *con = hctx->remote_conn; - + int ret; - - if (!host || + + if (!host || (!host->host->used || !host->port)) return -1; - + switch(hctx->state) { + case PROXY_STATE_CONNECT: + /* wait for the connect() to finish */ + + /* connect failed ? */ + if (-1 == hctx->fde_ndx) return HANDLER_ERROR; + + /* wait */ + return HANDLER_WAIT_FOR_EVENT; + + break; + case PROXY_STATE_INIT: - if (-1 == (hctx->fd = socket(AF_INET, SOCK_STREAM, 0))) { +#if defined(HAVE_IPV6) && defined(HAVE_INET_PTON) + if (strstr(host->host->ptr,":")) { + if (-1 == (hctx->fd = socket(AF_INET6, SOCK_STREAM, 0))) { log_error_write(srv, __FILE__, __LINE__, "ss", "socket failed: ", strerror(errno)); return HANDLER_ERROR; + } + } else +#endif + { + if (-1 == (hctx->fd = socket(AF_INET, SOCK_STREAM, 0))) { + log_error_write(srv, __FILE__, __LINE__, "ss", "socket failed: ", strerror(errno)); + return HANDLER_ERROR; + } } hctx->fde_ndx = -1; - + srv->cur_fds++; - + fdevent_register(srv->ev, hctx->fd, proxy_handle_fdevent, hctx); - + if (-1 == fdevent_fcntl_set(srv->ev, hctx->fd)) { log_error_write(srv, __FILE__, __LINE__, "ss", "fcntl failed: ", strerror(errno)); - + return HANDLER_ERROR; } - - /* fall through */ - - case PROXY_STATE_CONNECT: - /* try to finish the connect() */ - if (hctx->state == PROXY_STATE_INIT) { - /* first round */ - switch (proxy_establish_connection(srv, hctx)) { - case 1: - proxy_set_state(srv, hctx, PROXY_STATE_CONNECT); - - /* connection is in progress, wait for an event and call getsockopt() below */ - - fdevent_event_add(srv->ev, &(hctx->fde_ndx), hctx->fd, FDEVENT_OUT); - - return HANDLER_WAIT_FOR_EVENT; - case -1: - /* if ECONNREFUSED choose another connection -> FIXME */ - hctx->fde_ndx = -1; - - return HANDLER_ERROR; - default: - /* everything is ok, go on */ - break; - } - } else { - int socket_error; - socklen_t socket_error_len = sizeof(socket_error); - - /* we don't need it anymore */ - fdevent_event_del(srv->ev, &(hctx->fde_ndx), hctx->fd); - /* try to finish the connect() */ - if (0 != getsockopt(hctx->fd, SOL_SOCKET, SO_ERROR, &socket_error, &socket_error_len)) { - log_error_write(srv, __FILE__, __LINE__, "ss", - "getsockopt failed:", strerror(errno)); - - return HANDLER_ERROR; - } - if (socket_error != 0) { - log_error_write(srv, __FILE__, __LINE__, "ss", - "establishing connection failed:", strerror(socket_error), - "port:", hctx->host->port); - - return HANDLER_ERROR; - } - if (p->conf.debug) { - log_error_write(srv, __FILE__, __LINE__, "s", "proxy - connect - delayed success"); - } + switch (proxy_establish_connection(srv, hctx)) { + case 1: + proxy_set_state(srv, hctx, PROXY_STATE_CONNECT); + + /* connection is in progress, wait for an event and call getsockopt() below */ + + fdevent_event_set(srv->ev, &(hctx->fde_ndx), hctx->fd, FDEVENT_OUT); + + return HANDLER_WAIT_FOR_EVENT; + case -1: + /* if ECONNREFUSED choose another connection -> FIXME */ + hctx->fde_ndx = -1; + + return HANDLER_ERROR; + default: + /* everything is ok, go on */ + proxy_set_state(srv, hctx, PROXY_STATE_PREPARE_WRITE); + break; } - - proxy_set_state(srv, hctx, PROXY_STATE_PREPARE_WRITE); + /* fall through */ + case PROXY_STATE_PREPARE_WRITE: proxy_create_env(srv, hctx); - + proxy_set_state(srv, hctx, PROXY_STATE_WRITE); - + /* fall through */ case PROXY_STATE_WRITE:; - ret = srv->network_backend_write(srv, con, hctx->fd, hctx->wb); + ret = srv->network_backend_write(srv, con, hctx->fd, hctx->wb, MAX_WRITE_LIMIT); chunkqueue_remove_finished_chunks(hctx->wb); - if (-1 == ret) { - if (errno != EAGAIN && - errno != EINTR) { - log_error_write(srv, __FILE__, __LINE__, "ssd", "write failed:", strerror(errno), errno); - - return HANDLER_ERROR; - } else { - fdevent_event_add(srv->ev, &(hctx->fde_ndx), hctx->fd, FDEVENT_OUT); + if (-1 == ret) { /* error on our side */ + log_error_write(srv, __FILE__, __LINE__, "ssd", "write failed:", strerror(errno), errno); - return HANDLER_WAIT_FOR_EVENT; - } + return HANDLER_ERROR; + } else if (-2 == ret) { /* remote close */ + log_error_write(srv, __FILE__, __LINE__, "ssd", "write failed, remote connection close:", strerror(errno), errno); + + return HANDLER_ERROR; } if (hctx->wb->bytes_out == hctx->wb->bytes_in) { proxy_set_state(srv, hctx, PROXY_STATE_READ); fdevent_event_del(srv->ev, &(hctx->fde_ndx), hctx->fd); - fdevent_event_add(srv->ev, &(hctx->fde_ndx), hctx->fd, FDEVENT_IN); + fdevent_event_set(srv->ev, &(hctx->fde_ndx), hctx->fd, FDEVENT_IN); } else { - fdevent_event_add(srv->ev, &(hctx->fde_ndx), hctx->fd, FDEVENT_OUT); - + fdevent_event_set(srv->ev, &(hctx->fde_ndx), hctx->fd, FDEVENT_OUT); + return HANDLER_WAIT_FOR_EVENT; } - + return HANDLER_WAIT_FOR_EVENT; case PROXY_STATE_READ: /* waiting for a response */ @@ -851,7 +858,7 @@ static handler_t proxy_write_request(server *srv, handler_ctx *hctx) { log_error_write(srv, __FILE__, __LINE__, "s", "(debug) unknown state"); return HANDLER_ERROR; } - + return HANDLER_GO_ON; } @@ -860,23 +867,23 @@ static handler_t proxy_write_request(server *srv, handler_ctx *hctx) { static int mod_proxy_patch_connection(server *srv, connection *con, plugin_data *p) { size_t i, j; plugin_config *s = p->config_storage[0]; - + PATCH(extensions); PATCH(debug); PATCH(balance); - + /* skip the first, the global context */ for (i = 1; i < srv->config_context->used; i++) { data_config *dc = (data_config *)srv->config_context->data[i]; s = p->config_storage[i]; - + /* condition didn't match */ if (!config_check_cond(srv, con, dc)) continue; - + /* merge config */ for (j = 0; j < dc->value->used; j++) { data_unset *du = dc->value->data[j]; - + if (buffer_is_equal_string(du->key, CONST_STR_LEN("proxy.server"))) { PATCH(extensions); } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("proxy.debug"))) { @@ -886,60 +893,60 @@ static int mod_proxy_patch_connection(server *srv, connection *con, plugin_data } } } - + return 0; } #undef PATCH SUBREQUEST_FUNC(mod_proxy_handle_subrequest) { plugin_data *p = p_d; - + handler_ctx *hctx = con->plugin_ctx[p->id]; data_proxy *host; - + if (NULL == hctx) return HANDLER_GO_ON; mod_proxy_patch_connection(srv, con, p); - + host = hctx->host; - + /* not my job */ if (con->mode != p->id) return HANDLER_GO_ON; - + /* ok, create the request */ switch(proxy_write_request(srv, hctx)) { case HANDLER_ERROR: - log_error_write(srv, __FILE__, __LINE__, "sbdd", "proxy-server disabled:", + log_error_write(srv, __FILE__, __LINE__, "sbdd", "proxy-server disabled:", host->host, host->port, hctx->fd); - + /* disable this server */ host->is_disabled = 1; host->disable_ts = srv->cur_ts; - + proxy_connection_close(srv, hctx); - - /* reset the enviroment and restart the sub-request */ + + /* reset the enviroment and restart the sub-request */ buffer_reset(con->physical.path); con->mode = DIRECT; joblist_append(srv, con); - /* mis-using HANDLER_WAIT_FOR_FD to break out of the loop - * and hope that the childs will be restarted - * + /* mis-using HANDLER_WAIT_FOR_FD to break out of the loop + * and hope that the childs will be restarted + * */ return HANDLER_WAIT_FOR_FD; case HANDLER_WAIT_FOR_EVENT: - return HANDLER_WAIT_FOR_EVENT; + break; case HANDLER_WAIT_FOR_FD: return HANDLER_WAIT_FOR_FD; default: break; } - + if (con->file_started == 1) { return HANDLER_FINISHED; } else { @@ -947,18 +954,17 @@ SUBREQUEST_FUNC(mod_proxy_handle_subrequest) { } } -static handler_t proxy_handle_fdevent(void *s, void *ctx, int revents) { - server *srv = (server *)s; +static handler_t proxy_handle_fdevent(server *srv, void *ctx, int revents) { handler_ctx *hctx = ctx; connection *con = hctx->remote_conn; plugin_data *p = hctx->plugin_data; - - + + if ((revents & FDEVENT_IN) && hctx->state == PROXY_STATE_READ) { if (p->conf.debug) { - log_error_write(srv, __FILE__, __LINE__, "sd", + log_error_write(srv, __FILE__, __LINE__, "sd", "proxy: fdevent-in", hctx->state); } @@ -966,11 +972,9 @@ static handler_t proxy_handle_fdevent(void *s, void *ctx, int revents) { case 0: break; case 1: - hctx->host->usage--; - /* we are done */ proxy_connection_close(srv, hctx); - + joblist_append(srv, con); return HANDLER_FINISHED; case -1: @@ -983,69 +987,125 @@ static handler_t proxy_handle_fdevent(void *s, void *ctx, int revents) { /* response might have been already started, kill the connection */ connection_set_state(srv, con, CON_STATE_ERROR); } - + joblist_append(srv, con); return HANDLER_FINISHED; } } - + if (revents & FDEVENT_OUT) { if (p->conf.debug) { - log_error_write(srv, __FILE__, __LINE__, "sd", + log_error_write(srv, __FILE__, __LINE__, "sd", "proxy: fdevent-out", hctx->state); } - if (hctx->state == PROXY_STATE_CONNECT || + if (hctx->state == PROXY_STATE_CONNECT) { + int socket_error; + socklen_t socket_error_len = sizeof(socket_error); + + /* we don't need it anymore */ + fdevent_event_del(srv->ev, &(hctx->fde_ndx), hctx->fd); + hctx->fde_ndx = -1; + + /* try to finish the connect() */ + if (0 != getsockopt(hctx->fd, SOL_SOCKET, SO_ERROR, &socket_error, &socket_error_len)) { + log_error_write(srv, __FILE__, __LINE__, "ss", + "getsockopt failed:", strerror(errno)); + + joblist_append(srv, con); + return HANDLER_FINISHED; + } + if (socket_error != 0) { + log_error_write(srv, __FILE__, __LINE__, "ss", + "establishing connection failed:", strerror(socket_error), + "port:", hctx->host->port); + + joblist_append(srv, con); + return HANDLER_FINISHED; + } + if (p->conf.debug) { + log_error_write(srv, __FILE__, __LINE__, "s", "proxy - connect - delayed success"); + } + + proxy_set_state(srv, hctx, PROXY_STATE_PREPARE_WRITE); + } + + if (hctx->state == PROXY_STATE_PREPARE_WRITE || hctx->state == PROXY_STATE_WRITE) { /* we are allowed to send something out - * - * 1. in a unfinished connect() call + * + * 1. after a just finished connect() call * 2. in a unfinished write() call (long POST request) */ return mod_proxy_handle_subrequest(srv, con, p); } else { - log_error_write(srv, __FILE__, __LINE__, "sd", + log_error_write(srv, __FILE__, __LINE__, "sd", "proxy: out", hctx->state); } } - + /* perhaps this issue is already handled */ if (revents & FDEVENT_HUP) { if (p->conf.debug) { - log_error_write(srv, __FILE__, __LINE__, "sd", + log_error_write(srv, __FILE__, __LINE__, "sd", "proxy: fdevent-hup", hctx->state); } - + if (hctx->state == PROXY_STATE_CONNECT) { /* connect() -> EINPROGRESS -> HUP */ - + /** - * what is proxy is doing if it can't reach the next hop ? - * + * what is proxy is doing if it can't reach the next hop ? + * */ - - proxy_connection_close(srv, hctx); - joblist_append(srv, con); - - con->http_status = 503; - con->mode = DIRECT; - + + if (hctx->host) { + hctx->host->is_disabled = 1; + hctx->host->disable_ts = srv->cur_ts; + log_error_write(srv, __FILE__, __LINE__, "sbdd", "proxy-server disabled:", + hctx->host->host, + hctx->host->port, + hctx->fd); + + /* disable this server */ + hctx->host->is_disabled = 1; + hctx->host->disable_ts = srv->cur_ts; + + proxy_connection_close(srv, hctx); + + /* reset the enviroment and restart the sub-request */ + buffer_reset(con->physical.path); + con->mode = DIRECT; + + joblist_append(srv, con); + } else { + proxy_connection_close(srv, hctx); + joblist_append(srv, con); + + con->mode = DIRECT; + con->http_status = 503; + } + return HANDLER_FINISHED; } - con->file_finished = 1; + if (!con->file_finished) { + http_chunk_append_mem(srv, con, NULL, 0); + } + con->file_finished = 1; proxy_connection_close(srv, hctx); joblist_append(srv, con); } else if (revents & FDEVENT_ERR) { /* kill all connections to the proxy process */ - + log_error_write(srv, __FILE__, __LINE__, "sd", "proxy-FDEVENT_ERR, but no HUP", revents); + con->file_finished = 1; joblist_append(srv, con); proxy_connection_close(srv, hctx); } - + return HANDLER_FINISHED; } @@ -1059,69 +1119,82 @@ static handler_t mod_proxy_check_extension(server *srv, connection *con, void *p buffer *fn; data_array *extension = NULL; size_t path_info_offset; - + + if (con->mode != DIRECT) return HANDLER_GO_ON; + /* Possibly, we processed already this request */ if (con->file_started == 1) return HANDLER_GO_ON; - + mod_proxy_patch_connection(srv, con, p); - + fn = con->uri.path; if (fn->used == 0) { return HANDLER_ERROR; } - + s_len = fn->used - 1; - - + + path_info_offset = 0; - if (p->conf.debug) { + if (p->conf.debug) { log_error_write(srv, __FILE__, __LINE__, "s", "proxy - start"); } /* check if extension matches */ for (k = 0; k < p->conf.extensions->used; k++) { + data_array *ext = NULL; size_t ct_len; - - extension = (data_array *)p->conf.extensions->data[k]; - - if (extension->key->used == 0) continue; - - ct_len = extension->key->used - 1; - + + ext = (data_array *)p->conf.extensions->data[k]; + + if (ext->key->used == 0) continue; + + ct_len = ext->key->used - 1; + if (s_len < ct_len) continue; - + /* check extension in the form "/proxy_pattern" */ - if (*(extension->key->ptr) == '/' && strncmp(fn->ptr, extension->key->ptr, ct_len) == 0) { - if (s_len > ct_len + 1) { - char *pi_offset; - - if (0 != (pi_offset = strchr(fn->ptr + ct_len + 1, '/'))) { - path_info_offset = pi_offset - fn->ptr; + if (*(ext->key->ptr) == '/') { + if (strncmp(fn->ptr, ext->key->ptr, ct_len) == 0) { + if (s_len > ct_len + 1) { + char *pi_offset; + + if (NULL != (pi_offset = strchr(fn->ptr + ct_len + 1, '/'))) { + path_info_offset = pi_offset - fn->ptr; + } } + extension = ext; + break; } - break; - } else if (0 == strncmp(fn->ptr + s_len - ct_len, extension->key->ptr, ct_len)) { + } else if (0 == strncmp(fn->ptr + s_len - ct_len, ext->key->ptr, ct_len)) { /* check extension in the form ".fcg" */ + extension = ext; break; } } - - if (k == p->conf.extensions->used) { + + if (NULL == extension) { return HANDLER_GO_ON; } - if (p->conf.debug) { + if (p->conf.debug) { log_error_write(srv, __FILE__, __LINE__, "s", "proxy - ext found"); } - switch(p->conf.balance) { + if (extension->value->used == 1) { + if ( ((data_proxy *)extension->value->data[0])->is_disabled ) { + ndx = -1; + } else { + ndx = 0; + } + } else if (extension->value->used != 0) switch(p->conf.balance) { case PROXY_BALANCE_HASH: /* hash balancing */ if (p->conf.debug) { - log_error_write(srv, __FILE__, __LINE__, "sd", + log_error_write(srv, __FILE__, __LINE__, "sd", "proxy - used hash balancing, hosts:", extension->value->used); } @@ -1130,13 +1203,13 @@ static handler_t mod_proxy_check_extension(server *srv, connection *con, void *p unsigned long cur_max; if (host->is_disabled) continue; - + cur_max = generate_crc32c(CONST_BUF_LEN(con->uri.path)) + generate_crc32c(CONST_BUF_LEN(host->host)) + /* we can cache this */ generate_crc32c(CONST_BUF_LEN(con->uri.authority)); - + if (p->conf.debug) { - log_error_write(srv, __FILE__, __LINE__, "sbbbd", + log_error_write(srv, __FILE__, __LINE__, "sbbbd", "proxy - election:", con->uri.path, host->host, @@ -1156,88 +1229,92 @@ static handler_t mod_proxy_check_extension(server *srv, connection *con, void *p case PROXY_BALANCE_FAIR: /* fair balancing */ if (p->conf.debug) { - log_error_write(srv, __FILE__, __LINE__, "s", + log_error_write(srv, __FILE__, __LINE__, "s", "proxy - used fair balancing"); } for (k = 0, ndx = -1, max_usage = INT_MAX; k < extension->value->used; k++) { data_proxy *host = (data_proxy *)extension->value->data[k]; - + if (host->is_disabled) continue; if (host->usage < max_usage) { max_usage = host->usage; - + ndx = k; } } break; - case PROXY_BALANCE_RR: + case PROXY_BALANCE_RR: { + data_proxy *host; + /* round robin */ if (p->conf.debug) { - log_error_write(srv, __FILE__, __LINE__, "s", + log_error_write(srv, __FILE__, __LINE__, "s", "proxy - used round-robin balancing"); } /* just to be sure */ assert(extension->value->used < INT_MAX); - - for (k = 0, ndx = -1, max_usage = INT_MAX; k < extension->value->used; k++) { - data_proxy *host = (data_proxy *)extension->value->data[k]; - - if (host->is_disabled) continue; - /* first usable ndx */ - if (max_usage == INT_MAX) { - max_usage = k; - } + host = (data_proxy *)extension->value->data[0]; - /* get next ndx */ - if ((int)k > host->last_used_ndx) { - ndx = k; - host->last_used_ndx = k; + /* Use last_used_ndx from first host in list */ + k = host->last_used_ndx; + ndx = k + 1; /* use next host after the last one */ + if (ndx < 0) ndx = 0; - break; + /* Search first active host after last_used_ndx */ + while ( ndx < (int) extension->value->used + && (host = (data_proxy *)extension->value->data[ndx])->is_disabled ) ndx++; + + if (ndx >= (int) extension->value->used) { + /* didn't found a higher id, wrap to the start */ + for (ndx = 0; ndx <= (int) k; ndx++) { + host = (data_proxy *)extension->value->data[ndx]; + if (!host->is_disabled) break; } + + /* No active host found */ + if (host->is_disabled) ndx = -1; } - - /* didn't found a higher id, wrap to the start */ - if (ndx != -1 && max_usage != INT_MAX) { - ndx = max_usage; - } + + /* Save new index for next round */ + ((data_proxy *)extension->value->data[0])->last_used_ndx = ndx; break; + } default: break; } - + /* found a server */ if (ndx != -1) { data_proxy *host = (data_proxy *)extension->value->data[ndx]; - - /* - * if check-local is disabled, use the uri.path handler - * + + /* + * if check-local is disabled, use the uri.path handler + * */ - + /* init handler-context */ handler_ctx *hctx; hctx = handler_ctx_init(); - + hctx->path_info_offset = path_info_offset; hctx->remote_conn = con; hctx->plugin_data = p; hctx->host = host; - + con->plugin_ctx[p->id] = hctx; - + host->usage++; - + con->mode = p->id; - + if (p->conf.debug) { - log_error_write(srv, __FILE__, __LINE__, "sbd", + log_error_write(srv, __FILE__, __LINE__, "sbd", "proxy - found a host", host->host, host->port); } @@ -1246,11 +1323,11 @@ static handler_t mod_proxy_check_extension(server *srv, connection *con, void *p } else { /* no handler found */ con->http_status = 500; - - log_error_write(srv, __FILE__, __LINE__, "sb", - "no proxy-handler found for:", + + log_error_write(srv, __FILE__, __LINE__, "sb", + "no proxy-handler found for:", fn); - + return HANDLER_FINISHED; } return HANDLER_GO_ON; @@ -1258,7 +1335,7 @@ static handler_t mod_proxy_check_extension(server *srv, connection *con, void *p static handler_t mod_proxy_connection_close_callback(server *srv, connection *con, void *p_d) { plugin_data *p = p_d; - + proxy_connection_close(srv, con->plugin_ctx[p->id]); return HANDLER_GO_ON; @@ -1277,11 +1354,11 @@ TRIGGER_FUNC(mod_proxy_trigger) { size_t i, n, k; for (i = 0; i < srv->config_context->used; i++) { plugin_config *s = p->config_storage[i]; - - if (!s) continue; + + if (!s) continue; /* get the extensions for all configs */ - + for (k = 0; k < s->extensions->used; k++) { data_array *extension = (data_array *)s->extensions->data[k]; @@ -1291,8 +1368,8 @@ TRIGGER_FUNC(mod_proxy_trigger) { if (!host->is_disabled || srv->cur_ts - host->disable_ts < 5) continue; - - log_error_write(srv, __FILE__, __LINE__, "sbd", + + log_error_write(srv, __FILE__, __LINE__, "sbd", "proxy - re-enabled:", host->host, host->port); @@ -1306,6 +1383,7 @@ TRIGGER_FUNC(mod_proxy_trigger) { } +int mod_proxy_plugin_init(plugin *p); int mod_proxy_plugin_init(plugin *p) { p->version = LIGHTTPD_VERSION_ID; p->name = buffer_init_string("proxy"); @@ -1318,8 +1396,8 @@ int mod_proxy_plugin_init(plugin *p) { p->handle_uri_clean = mod_proxy_check_extension; p->handle_subrequest = mod_proxy_handle_subrequest; p->handle_trigger = mod_proxy_trigger; - + p->data = NULL; - + return 0; } diff --git a/src/mod_redirect.c b/src/mod_redirect.c index 6040631..5d51b86 100644 --- a/src/mod_redirect.c +++ b/src/mod_redirect.c @@ -1,7 +1,3 @@ -#include <ctype.h> -#include <stdlib.h> -#include <string.h> - #include "base.h" #include "log.h" #include "buffer.h" @@ -9,48 +5,50 @@ #include "plugin.h" #include "response.h" -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif +#include <ctype.h> +#include <stdlib.h> +#include <string.h> typedef struct { pcre_keyvalue_buffer *redirect; data_config *context; /* to which apply me */ + + unsigned short redirect_code; } plugin_config; typedef struct { PLUGIN_DATA; buffer *match_buf; buffer *location; - + plugin_config **config_storage; - - plugin_config conf; + + plugin_config conf; } plugin_data; INIT_FUNC(mod_redirect_init) { plugin_data *p; - + p = calloc(1, sizeof(*p)); - + p->match_buf = buffer_init(); p->location = buffer_init(); - + return p; } FREE_FUNC(mod_redirect_free) { plugin_data *p = p_d; - + if (!p) return HANDLER_GO_ON; if (p->config_storage) { size_t i; for (i = 0; i < srv->config_context->used; i++) { plugin_config *s = p->config_storage[i]; - + pcre_keyvalue_buffer_free(s->redirect); - + free(s); } free(p->config_storage); @@ -59,9 +57,9 @@ FREE_FUNC(mod_redirect_free) { buffer_free(p->match_buf); buffer_free(p->location); - + free(p); - + return HANDLER_GO_ON; } @@ -69,97 +67,104 @@ SETDEFAULTS_FUNC(mod_redirect_set_defaults) { plugin_data *p = p_d; data_unset *du; size_t i = 0; - - config_values_t cv[] = { + + config_values_t cv[] = { { "url.redirect", NULL, T_CONFIG_LOCAL, T_CONFIG_SCOPE_CONNECTION }, /* 0 */ + { "url.redirect-code", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 1 */ { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET } }; - + if (!p) return HANDLER_ERROR; - + /* 0 */ p->config_storage = calloc(1, srv->config_context->used * sizeof(specific_config *)); - + for (i = 0; i < srv->config_context->used; i++) { plugin_config *s; size_t j; array *ca; data_array *da = (data_array *)du; - + s = calloc(1, sizeof(plugin_config)); s->redirect = pcre_keyvalue_buffer_init(); - + s->redirect_code = 301; + cv[0].destination = s->redirect; - + cv[1].destination = &(s->redirect_code); + p->config_storage[i] = s; ca = ((data_config *)srv->config_context->data[i])->value; - + if (0 != config_insert_values_global(srv, ca, cv)) { return HANDLER_ERROR; } - + if (NULL == (du = array_get_element(ca, "url.redirect"))) { /* no url.redirect defined */ continue; } - + if (du->type != TYPE_ARRAY) { - log_error_write(srv, __FILE__, __LINE__, "sss", + log_error_write(srv, __FILE__, __LINE__, "sss", "unexpected type for key: ", "url.redirect", "array of strings"); - + return HANDLER_ERROR; } - + da = (data_array *)du; - + for (j = 0; j < da->value->used; j++) { if (da->value->data[j]->type != TYPE_STRING) { - log_error_write(srv, __FILE__, __LINE__, "sssbs", - "unexpected type for key: ", - "url.redirect", + log_error_write(srv, __FILE__, __LINE__, "sssbs", + "unexpected type for key: ", + "url.redirect", "[", da->value->data[j]->key, "](string)"); - + return HANDLER_ERROR; } - - if (0 != pcre_keyvalue_buffer_append(s->redirect, + + if (0 != pcre_keyvalue_buffer_append(srv, s->redirect, ((data_string *)(da->value->data[j]))->key->ptr, ((data_string *)(da->value->data[j]))->value->ptr)) { - - log_error_write(srv, __FILE__, __LINE__, "sb", + + log_error_write(srv, __FILE__, __LINE__, "sb", "pcre-compile failed for", da->value->data[j]->key); } } } - + return HANDLER_GO_ON; } #ifdef HAVE_PCRE_H static int mod_redirect_patch_connection(server *srv, connection *con, plugin_data *p) { size_t i, j; plugin_config *s = p->config_storage[0]; - + p->conf.redirect = s->redirect; - + p->conf.redirect_code = s->redirect_code; + p->conf.context = NULL; + /* skip the first, the global context */ for (i = 1; i < srv->config_context->used; i++) { data_config *dc = (data_config *)srv->config_context->data[i]; s = p->config_storage[i]; - + /* condition didn't match */ if (!config_check_cond(srv, con, dc)) continue; - + /* merge config */ for (j = 0; j < dc->value->used; j++) { data_unset *du = dc->value->data[j]; - + if (0 == strcmp(du->key->ptr, "url.redirect")) { p->conf.redirect = s->redirect; p->conf.context = dc; + } else if (0 == strcmp(du->key->ptr, "url.redirect-code")) { + p->conf.redirect_code = s->redirect_code; } } } - + return 0; } #endif @@ -168,17 +173,17 @@ static handler_t mod_redirect_uri_handler(server *srv, connection *con, void *p_ plugin_data *p = p_data; size_t i; - /* + /* * REWRITE URL - * + * * e.g. redirect /base/ to /index.php?section=base - * + * */ - + mod_redirect_patch_connection(srv, con, p); - + buffer_copy_string_buffer(p->match_buf, con->request.uri); - + for (i = 0; i < p->conf.redirect->used; i++) { pcre *match; pcre_extra *extra; @@ -188,12 +193,12 @@ static handler_t mod_redirect_uri_handler(server *srv, connection *con, void *p_ pcre_keyvalue *kv = p->conf.redirect->kv[i]; # define N 10 int ovec[N * 3]; - + match = kv->key; extra = kv->key_extra; pattern = kv->value->ptr; pattern_len = kv->value->used - 1; - + if ((n = pcre_exec(match, extra, p->match_buf->ptr, p->match_buf->used - 1, 0, 0, ovec, 3 * N)) < 0) { if (n != PCRE_ERROR_NOMATCH) { log_error_write(srv, __FILE__, __LINE__, "sd", @@ -202,75 +207,83 @@ static handler_t mod_redirect_uri_handler(server *srv, connection *con, void *p_ } } else { const char **list; - size_t start, end; + size_t start; size_t k; - + /* it matched */ pcre_get_substring_list(p->match_buf->ptr, ovec, n, &list); - + /* search for $[0-9] */ - + buffer_reset(p->location); - - start = 0; end = pattern_len; - for (k = 0; k < pattern_len; k++) { - if ((pattern[k] == '$' || pattern[k] == '%') && - isdigit((unsigned char)pattern[k + 1])) { + + start = 0; + for (k = 0; k + 1 < pattern_len; k++) { + if (pattern[k] == '$' || pattern[k] == '%') { /* got one */ - + size_t num = pattern[k + 1] - '0'; - - end = k; - - buffer_append_string_len(p->location, pattern + start, end - start); - - if (pattern[k] == '$') { + + buffer_append_string_len(p->location, pattern + start, k - start); + + if (!isdigit((unsigned char)pattern[k + 1])) { + /* enable escape: "%%" => "%", "%a" => "%a", "$$" => "$" */ + buffer_append_string_len(p->location, pattern+k, pattern[k] == pattern[k+1] ? 1 : 2); + } else if (pattern[k] == '$') { /* n is always > 0 */ if (num < (size_t)n) { buffer_append_string(p->location, list[num]); } + } else if (p->conf.context == NULL) { + /* we have no context, we are global */ + log_error_write(srv, __FILE__, __LINE__, "sb", + "used a rewrite containing a %[0-9]+ in the global scope, ignored:", + kv->value); } else { config_append_cond_match_buffer(con, p->conf.context, p->location, num); } - + k++; start = k + 1; - } + } } - + buffer_append_string_len(p->location, pattern + start, pattern_len - start); - + pcre_free(list); - + response_header_insert(srv, con, CONST_STR_LEN("Location"), CONST_BUF_LEN(p->location)); - - con->http_status = 301; - + + con->http_status = p->conf.redirect_code > 99 && p->conf.redirect_code < 1000 ? p->conf.redirect_code : 301; + con->mode = DIRECT; + con->file_finished = 1; + return HANDLER_FINISHED; } } #undef N - + #else UNUSED(srv); UNUSED(con); UNUSED(p_data); #endif - + return HANDLER_GO_ON; } +int mod_redirect_plugin_init(plugin *p); int mod_redirect_plugin_init(plugin *p) { p->version = LIGHTTPD_VERSION_ID; p->name = buffer_init_string("redirect"); - + p->init = mod_redirect_init; p->handle_uri_clean = mod_redirect_uri_handler; p->set_defaults = mod_redirect_set_defaults; p->cleanup = mod_redirect_free; - + p->data = NULL; - + return 0; } diff --git a/src/mod_rewrite.c b/src/mod_rewrite.c index ff152a9..9672c4e 100644 --- a/src/mod_rewrite.c +++ b/src/mod_rewrite.c @@ -1,37 +1,34 @@ -#include <ctype.h> -#include <stdlib.h> -#include <string.h> - #include "base.h" #include "log.h" #include "buffer.h" #include "plugin.h" +#include "stat_cache.h" -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif +#include <ctype.h> +#include <stdlib.h> +#include <string.h> -typedef struct { #ifdef HAVE_PCRE_H +typedef struct { pcre *key; -#endif - + buffer *value; - + int once; } rewrite_rule; typedef struct { rewrite_rule **ptr; - + size_t used; size_t size; } rewrite_rule_buffer; typedef struct { rewrite_rule_buffer *rewrite; - data_config *context; /* to which apply me */ + rewrite_rule_buffer *rewrite_NF; + data_config *context, *context_NF; /* to which apply me */ } plugin_config; typedef struct { @@ -42,20 +39,20 @@ typedef struct { typedef struct { PLUGIN_DATA; buffer *match_buf; - + plugin_config **config_storage; - - plugin_config conf; + + plugin_config conf; } plugin_data; -static handler_ctx * handler_ctx_init() { +static handler_ctx * handler_ctx_init(void) { handler_ctx * hctx; - + hctx = calloc(1, sizeof(*hctx)); - + hctx->state = REWRITE_STATE_UNSET; hctx->loops = 0; - + return hctx; } @@ -63,66 +60,56 @@ static void handler_ctx_free(handler_ctx *hctx) { free(hctx); } -rewrite_rule_buffer *rewrite_rule_buffer_init(void) { +static rewrite_rule_buffer *rewrite_rule_buffer_init(void) { rewrite_rule_buffer *kvb; - + kvb = calloc(1, sizeof(*kvb)); - + return kvb; } -int rewrite_rule_buffer_append(rewrite_rule_buffer *kvb, buffer *key, buffer *value, int once) { -#ifdef HAVE_PCRE_H +static int rewrite_rule_buffer_append(rewrite_rule_buffer *kvb, buffer *key, buffer *value, int once) { size_t i; const char *errptr; int erroff; - + if (!key) return -1; if (kvb->size == 0) { kvb->size = 4; kvb->used = 0; - + kvb->ptr = malloc(kvb->size * sizeof(*kvb->ptr)); - + for(i = 0; i < kvb->size; i++) { kvb->ptr[i] = calloc(1, sizeof(**kvb->ptr)); } } else if (kvb->used == kvb->size) { kvb->size += 4; - + kvb->ptr = realloc(kvb->ptr, kvb->size * sizeof(*kvb->ptr)); - + for(i = kvb->used; i < kvb->size; i++) { kvb->ptr[i] = calloc(1, sizeof(**kvb->ptr)); } } - + if (NULL == (kvb->ptr[kvb->used]->key = pcre_compile(key->ptr, 0, &errptr, &erroff, NULL))) { - + return -1; } - + kvb->ptr[kvb->used]->value = buffer_init(); buffer_copy_string_buffer(kvb->ptr[kvb->used]->value, value); kvb->ptr[kvb->used]->once = once; - + kvb->used++; - - return 0; -#else - UNUSED(kvb); - UNUSED(value); - UNUSED(once); - UNUSED(key); - return -1; -#endif + return 0; } -void rewrite_rule_buffer_free(rewrite_rule_buffer *kvb) { -#ifdef HAVE_PCRE_H +static void rewrite_rule_buffer_free(rewrite_rule_buffer *kvb) { size_t i; for (i = 0; i < kvb->size; i++) { @@ -130,239 +117,264 @@ void rewrite_rule_buffer_free(rewrite_rule_buffer *kvb) { if (kvb->ptr[i]->value) buffer_free(kvb->ptr[i]->value); free(kvb->ptr[i]); } - + if (kvb->ptr) free(kvb->ptr); -#endif - + free(kvb); } INIT_FUNC(mod_rewrite_init) { plugin_data *p; - + p = calloc(1, sizeof(*p)); - + p->match_buf = buffer_init(); - + return p; } FREE_FUNC(mod_rewrite_free) { plugin_data *p = p_d; - + UNUSED(srv); if (!p) return HANDLER_GO_ON; - + buffer_free(p->match_buf); if (p->config_storage) { size_t i; for (i = 0; i < srv->config_context->used; i++) { plugin_config *s = p->config_storage[i]; rewrite_rule_buffer_free(s->rewrite); - + rewrite_rule_buffer_free(s->rewrite_NF); + free(s); } free(p->config_storage); } - + free(p); - + return HANDLER_GO_ON; } -static int parse_config_entry(server *srv, plugin_config *s, array *ca, const char *option, int once) { +static int parse_config_entry(server *srv, array *ca, rewrite_rule_buffer *kvb, const char *option, int once) { data_unset *du; - + if (NULL != (du = array_get_element(ca, option))) { - data_array *da = (data_array *)du; + data_array *da; size_t j; - + if (du->type != TYPE_ARRAY) { - log_error_write(srv, __FILE__, __LINE__, "sss", + log_error_write(srv, __FILE__, __LINE__, "sss", "unexpected type for key: ", option, "array of strings"); - + return HANDLER_ERROR; } - + da = (data_array *)du; - + for (j = 0; j < da->value->used; j++) { if (da->value->data[j]->type != TYPE_STRING) { - log_error_write(srv, __FILE__, __LINE__, "sssbs", - "unexpected type for key: ", - option, + log_error_write(srv, __FILE__, __LINE__, "sssbs", + "unexpected type for key: ", + option, "[", da->value->data[j]->key, "](string)"); - + return HANDLER_ERROR; } - - if (0 != rewrite_rule_buffer_append(s->rewrite, + + if (0 != rewrite_rule_buffer_append(kvb, ((data_string *)(da->value->data[j]))->key, ((data_string *)(da->value->data[j]))->value, once)) { -#ifdef HAVE_PCRE_H - log_error_write(srv, __FILE__, __LINE__, "sb", + log_error_write(srv, __FILE__, __LINE__, "sb", "pcre-compile failed for", da->value->data[j]->key); -#else - log_error_write(srv, __FILE__, __LINE__, "s", - "pcre support is missing, please install libpcre and the headers"); -#endif } } } - + + return 0; +} +#else +static int parse_config_entry(server *srv, array *ca, const char *option) { + static int logged_message = 0; + if (logged_message) return 0; + if (NULL != array_get_element(ca, option)) { + logged_message = 1; + log_error_write(srv, __FILE__, __LINE__, "s", + "pcre support is missing, please install libpcre and the headers"); + } return 0; } +#endif SETDEFAULTS_FUNC(mod_rewrite_set_defaults) { - plugin_data *p = p_d; size_t i = 0; - - config_values_t cv[] = { + config_values_t cv[] = { { "url.rewrite-repeat", NULL, T_CONFIG_LOCAL, T_CONFIG_SCOPE_CONNECTION }, /* 0 */ { "url.rewrite-once", NULL, T_CONFIG_LOCAL, T_CONFIG_SCOPE_CONNECTION }, /* 1 */ - - /* old names, still supported - * + + /* these functions only rewrite if the target is not already in the filestore + * + * url.rewrite-repeat-if-not-file is the equivalent of url.rewrite-repeat + * url.rewrite-if-not-file is the equivalent of url.rewrite-once + * + */ + { "url.rewrite-repeat-if-not-file", NULL, T_CONFIG_LOCAL, T_CONFIG_SCOPE_CONNECTION }, /* 2 */ + { "url.rewrite-if-not-file", NULL, T_CONFIG_LOCAL, T_CONFIG_SCOPE_CONNECTION }, /* 3 */ + + /* old names, still supported + * * url.rewrite remapped to url.rewrite-once * url.rewrite-final is url.rewrite-once - * + * */ - { "url.rewrite", NULL, T_CONFIG_LOCAL, T_CONFIG_SCOPE_CONNECTION }, /* 2 */ - { "url.rewrite-final", NULL, T_CONFIG_LOCAL, T_CONFIG_SCOPE_CONNECTION }, /* 3 */ + { "url.rewrite", NULL, T_CONFIG_LOCAL, T_CONFIG_SCOPE_CONNECTION }, /* 4 */ + { "url.rewrite-final", NULL, T_CONFIG_LOCAL, T_CONFIG_SCOPE_CONNECTION }, /* 5 */ { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET } }; - + +#ifdef HAVE_PCRE_H + plugin_data *p = p_d; + if (!p) return HANDLER_ERROR; - + /* 0 */ p->config_storage = calloc(1, srv->config_context->used * sizeof(specific_config *)); - +#else + UNUSED(p_d); +#endif + for (i = 0; i < srv->config_context->used; i++) { - plugin_config *s; array *ca; - +#ifdef HAVE_PCRE_H + plugin_config *s; + s = calloc(1, sizeof(plugin_config)); - s->rewrite = rewrite_rule_buffer_init(); - - cv[0].destination = s->rewrite; - cv[1].destination = s->rewrite; - cv[2].destination = s->rewrite; - + s->rewrite = rewrite_rule_buffer_init(); + s->rewrite_NF = rewrite_rule_buffer_init(); p->config_storage[i] = s; +#endif + ca = ((data_config *)srv->config_context->data[i])->value; - + if (0 != config_insert_values_global(srv, ca, cv)) { return HANDLER_ERROR; } - - parse_config_entry(srv, s, ca, "url.rewrite-once", 1); - parse_config_entry(srv, s, ca, "url.rewrite-final", 1); - parse_config_entry(srv, s, ca, "url.rewrite", 1); - parse_config_entry(srv, s, ca, "url.rewrite-repeat", 0); + +#ifndef HAVE_PCRE_H +# define parse_config_entry(srv, ca, x, option, y) parse_config_entry(srv, ca, option) +#endif + parse_config_entry(srv, ca, s->rewrite, "url.rewrite-once", 1); + parse_config_entry(srv, ca, s->rewrite, "url.rewrite-final", 1); + parse_config_entry(srv, ca, s->rewrite_NF, "url.rewrite-if-not-file", 1); + parse_config_entry(srv, ca, s->rewrite_NF, "url.rewrite-repeat-if-not-file", 0); + parse_config_entry(srv, ca, s->rewrite, "url.rewrite", 1); + parse_config_entry(srv, ca, s->rewrite, "url.rewrite-repeat", 0); } - + return HANDLER_GO_ON; } + #ifdef HAVE_PCRE_H + +#define PATCH(x) \ + p->conf.x = s->x; static int mod_rewrite_patch_connection(server *srv, connection *con, plugin_data *p) { size_t i, j; plugin_config *s = p->config_storage[0]; - p->conf.rewrite = s->rewrite; - + + PATCH(rewrite); + PATCH(rewrite_NF); + p->conf.context = NULL; + p->conf.context_NF = NULL; + /* skip the first, the global context */ for (i = 1; i < srv->config_context->used; i++) { data_config *dc = (data_config *)srv->config_context->data[i]; s = p->config_storage[i]; - + if (COMP_HTTP_URL == dc->comp) continue; - + /* condition didn't match */ if (!config_check_cond(srv, con, dc)) continue; - + /* merge config */ for (j = 0; j < dc->value->used; j++) { data_unset *du = dc->value->data[j]; - + if (buffer_is_equal_string(du->key, CONST_STR_LEN("url.rewrite"))) { - p->conf.rewrite = s->rewrite; + PATCH(rewrite); p->conf.context = dc; } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("url.rewrite-once"))) { - p->conf.rewrite = s->rewrite; + PATCH(rewrite); p->conf.context = dc; } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("url.rewrite-repeat"))) { - p->conf.rewrite = s->rewrite; + PATCH(rewrite); p->conf.context = dc; + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("url.rewrite-if-not-file"))) { + PATCH(rewrite_NF); + p->conf.context_NF = dc; + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("url.rewrite-repeat-if-not-file"))) { + PATCH(rewrite_NF); + p->conf.context_NF = dc; } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("url.rewrite-final"))) { - p->conf.rewrite = s->rewrite; + PATCH(rewrite); p->conf.context = dc; } } } - + return 0; } -#endif + URIHANDLER_FUNC(mod_rewrite_con_reset) { plugin_data *p = p_d; - + UNUSED(srv); - + if (con->plugin_ctx[p->id]) { handler_ctx_free(con->plugin_ctx[p->id]); con->plugin_ctx[p->id] = NULL; } - + return HANDLER_GO_ON; } -URIHANDLER_FUNC(mod_rewrite_uri_handler) { -#ifdef HAVE_PCRE_H - plugin_data *p = p_d; +static int process_rewrite_rules(server *srv, connection *con, plugin_data *p, rewrite_rule_buffer *kvb) { size_t i; handler_ctx *hctx; - /* - * REWRITE URL - * - * e.g. rewrite /base/ to /index.php?section=base - * - */ - if (con->plugin_ctx[p->id]) { hctx = con->plugin_ctx[p->id]; - + if (hctx->loops++ > 100) { - log_error_write(srv, __FILE__, __LINE__, "s", + log_error_write(srv, __FILE__, __LINE__, "s", "ENDLESS LOOP IN rewrite-rule DETECTED ... aborting request, perhaps you want to use url.rewrite-once instead of url.rewrite-repeat"); - + return HANDLER_ERROR; } - + if (hctx->state == REWRITE_STATE_FINISHED) return HANDLER_GO_ON; } - - mod_rewrite_patch_connection(srv, con, p); - if (!p->conf.rewrite) return HANDLER_GO_ON; - buffer_copy_string_buffer(p->match_buf, con->request.uri); - - for (i = 0; i < p->conf.rewrite->used; i++) { + + for (i = 0; i < kvb->used; i++) { pcre *match; const char *pattern; size_t pattern_len; int n; - rewrite_rule *rule = p->conf.rewrite->ptr[i]; + rewrite_rule *rule = kvb->ptr[i]; # define N 10 int ovec[N * 3]; - + match = rule->key; pattern = rule->value->ptr; pattern_len = rule->value->used - 1; - + if ((n = pcre_exec(match, NULL, p->match_buf->ptr, p->match_buf->used - 1, 0, 0, ovec, 3 * N)) < 0) { if (n != PCRE_ERROR_NOMATCH) { log_error_write(srv, __FILE__, __LINE__, "sd", @@ -371,80 +383,128 @@ URIHANDLER_FUNC(mod_rewrite_uri_handler) { } } else { const char **list; - size_t start, end; + size_t start; size_t k; - + /* it matched */ pcre_get_substring_list(p->match_buf->ptr, ovec, n, &list); - + /* search for $[0-9] */ - + buffer_reset(con->request.uri); - - start = 0; end = pattern_len; - for (k = 0; k < pattern_len; k++) { - if ((pattern[k] == '$' || pattern[k] == '%') && - isdigit((unsigned char)pattern[k + 1])) { + + start = 0; + for (k = 0; k+1 < pattern_len; k++) { + if (pattern[k] == '$' || pattern[k] == '%') { /* got one */ - + size_t num = pattern[k + 1] - '0'; - - end = k; - - buffer_append_string_len(con->request.uri, pattern + start, end - start); - - if (pattern[k] == '$') { + + buffer_append_string_len(con->request.uri, pattern + start, k - start); + + if (!isdigit((unsigned char)pattern[k + 1])) { + /* enable escape: "%%" => "%", "%a" => "%a", "$$" => "$" */ + buffer_append_string_len(con->request.uri, pattern+k, pattern[k] == pattern[k+1] ? 1 : 2); + } else if (pattern[k] == '$') { /* n is always > 0 */ if (num < (size_t)n) { buffer_append_string(con->request.uri, list[num]); } + } else if (p->conf.context == NULL) { + /* we have no context, we are global */ + log_error_write(srv, __FILE__, __LINE__, "sb", + "used a redirect containing a %[0-9]+ in the global scope, ignored:", + rule->value); + } else { config_append_cond_match_buffer(con, p->conf.context, con->request.uri, num); } - + k++; start = k + 1; - } + } } - + buffer_append_string_len(con->request.uri, pattern + start, pattern_len - start); - + pcre_free(list); - - hctx = handler_ctx_init(); - - con->plugin_ctx[p->id] = hctx; - + + if (con->plugin_ctx[p->id] == NULL) { + hctx = handler_ctx_init(); + con->plugin_ctx[p->id] = hctx; + } else { + hctx = con->plugin_ctx[p->id]; + } + if (rule->once) hctx->state = REWRITE_STATE_FINISHED; - + return HANDLER_COMEBACK; } - } #undef N - -#else - UNUSED(srv); - UNUSED(con); - UNUSED(p_d); -#endif + } + + return HANDLER_GO_ON; +} + +URIHANDLER_FUNC(mod_rewrite_physical) { + plugin_data *p = p_d; + handler_t r; + stat_cache_entry *sce; + + if (con->mode != DIRECT) return HANDLER_GO_ON; + + mod_rewrite_patch_connection(srv, con, p); + p->conf.context = p->conf.context_NF; + + if (!p->conf.rewrite_NF) return HANDLER_GO_ON; + + /* skip if physical.path is a regular file */ + sce = NULL; + if (HANDLER_ERROR != stat_cache_get_entry(srv, con, con->physical.path, &sce)) { + if (S_ISREG(sce->st.st_mode)) return HANDLER_GO_ON; + } + + switch(r = process_rewrite_rules(srv, con, p, p->conf.rewrite_NF)) { + case HANDLER_COMEBACK: + buffer_reset(con->physical.path); + default: + return r; + } return HANDLER_GO_ON; } +URIHANDLER_FUNC(mod_rewrite_uri_handler) { + plugin_data *p = p_d; + + mod_rewrite_patch_connection(srv, con, p); + + if (!p->conf.rewrite) return HANDLER_GO_ON; + + return process_rewrite_rules(srv, con, p, p->conf.rewrite); + + return HANDLER_GO_ON; +} +#endif + +int mod_rewrite_plugin_init(plugin *p); int mod_rewrite_plugin_init(plugin *p) { p->version = LIGHTTPD_VERSION_ID; p->name = buffer_init_string("rewrite"); - + +#ifdef HAVE_PCRE_H p->init = mod_rewrite_init; /* it has to stay _raw as we are matching on uri + querystring */ - + p->handle_uri_raw = mod_rewrite_uri_handler; - p->set_defaults = mod_rewrite_set_defaults; + p->handle_physical = mod_rewrite_physical; p->cleanup = mod_rewrite_free; p->connection_reset = mod_rewrite_con_reset; - +#endif + p->set_defaults = mod_rewrite_set_defaults; + p->data = NULL; - + return 0; } diff --git a/src/mod_rrdtool.c b/src/mod_rrdtool.c index c7b897a..67a0215 100644 --- a/src/mod_rrdtool.c +++ b/src/mod_rrdtool.c @@ -1,4 +1,10 @@ -#define _GNU_SOURCE +#include "server.h" +#include "connections.h" +#include "response.h" +#include "connections.h" +#include "log.h" + +#include "plugin.h" #include <sys/types.h> #include <fcntl.h> @@ -9,13 +15,6 @@ #include <errno.h> #include <time.h> -#include "server.h" -#include "connections.h" -#include "response.h" -#include "connections.h" -#include "log.h" - -#include "plugin.h" #ifdef HAVE_FORK /* no need for waitpid if we don't have fork */ #include <sys/wait.h> @@ -23,7 +22,7 @@ typedef struct { buffer *path_rrdtool_bin; buffer *path_rrd; - + double requests, *requests_ptr; double bytes_written, *bytes_written_ptr; double bytes_read, *bytes_read_ptr; @@ -31,84 +30,84 @@ typedef struct { typedef struct { PLUGIN_DATA; - + buffer *cmd; buffer *resp; - + int read_fd, write_fd; pid_t rrdtool_pid; - + int rrdtool_running; - + plugin_config **config_storage; plugin_config conf; } plugin_data; INIT_FUNC(mod_rrd_init) { plugin_data *p; - + p = calloc(1, sizeof(*p)); - + p->resp = buffer_init(); p->cmd = buffer_init(); - + return p; } FREE_FUNC(mod_rrd_free) { plugin_data *p = p_d; size_t i; - + if (!p) return HANDLER_GO_ON; - + if (p->config_storage) { for (i = 0; i < srv->config_context->used; i++) { plugin_config *s = p->config_storage[i]; - + buffer_free(s->path_rrdtool_bin); buffer_free(s->path_rrd); - + free(s); } } buffer_free(p->cmd); buffer_free(p->resp); - + free(p->config_storage); - + if (p->rrdtool_pid) { int status; close(p->read_fd); close(p->write_fd); -#ifdef HAVE_FORK +#ifdef HAVE_FORK /* collect status */ waitpid(p->rrdtool_pid, &status, 0); #endif } - + free(p); - + return HANDLER_GO_ON; } -int mod_rrd_create_pipe(server *srv, plugin_data *p) { +static int mod_rrd_create_pipe(server *srv, plugin_data *p) { +#ifdef HAVE_FORK pid_t pid; - + int to_rrdtool_fds[2]; int from_rrdtool_fds[2]; -#ifdef HAVE_FORK if (pipe(to_rrdtool_fds)) { - log_error_write(srv, __FILE__, __LINE__, "ss", + log_error_write(srv, __FILE__, __LINE__, "ss", "pipe failed: ", strerror(errno)); return -1; } - + if (pipe(from_rrdtool_fds)) { - log_error_write(srv, __FILE__, __LINE__, "ss", + log_error_write(srv, __FILE__, __LINE__, "ss", "pipe failed: ", strerror(errno)); return -1; } - + /* fork, execve */ switch (pid = fork()) { case 0: { @@ -117,47 +116,40 @@ int mod_rrd_create_pipe(server *srv, plugin_data *p) { int argc; int i = 0; char *dash = "-"; - + /* move stdout to from_rrdtool_fd[1] */ close(STDOUT_FILENO); dup2(from_rrdtool_fds[1], STDOUT_FILENO); close(from_rrdtool_fds[1]); /* not needed */ close(from_rrdtool_fds[0]); - + /* move the stdin to to_rrdtool_fd[0] */ close(STDIN_FILENO); dup2(to_rrdtool_fds[0], STDIN_FILENO); close(to_rrdtool_fds[0]); /* not needed */ close(to_rrdtool_fds[1]); - - close(STDERR_FILENO); - - if (srv->errorlog_mode == ERRORLOG_FILE) { - dup2(srv->errorlog_fd, STDERR_FILENO); - close(srv->errorlog_fd); - } - + /* set up args */ argc = 3; args = malloc(sizeof(*args) * argc); i = 0; - + args[i++] = p->conf.path_rrdtool_bin->ptr; args[i++] = dash; - args[i++] = NULL; + args[i ] = NULL; /* we don't need the client socket */ for (i = 3; i < 256; i++) { close(i); } - + /* exec the cgi */ execv(args[0], args); - - log_error_write(srv, __FILE__, __LINE__, "sss", "spawing rrdtool failed: ", strerror(errno), args[0]); - + + /* log_error_write(srv, __FILE__, __LINE__, "sss", "spawing rrdtool failed: ", strerror(errno), args[0]); */ + /* */ SEGFAULT(); break; @@ -168,85 +160,136 @@ int mod_rrd_create_pipe(server *srv, plugin_data *p) { break; default: { /* father */ - + close(from_rrdtool_fds[1]); close(to_rrdtool_fds[0]); - + /* register PID and wait for them asyncronously */ p->write_fd = to_rrdtool_fds[1]; p->read_fd = from_rrdtool_fds[0]; p->rrdtool_pid = pid; - + +#ifdef FD_CLOEXEC + fcntl(p->write_fd, F_SETFD, FD_CLOEXEC); + fcntl(p->read_fd, F_SETFD, FD_CLOEXEC); +#endif + break; } } - + return 0; #else return -1; #endif } +/* read/write wrappers to catch EINTR */ + +/* write to blocking socket; blocks until all data is sent, write returns 0 or an error (apart from EINTR) occurs. */ +static ssize_t safe_write(int fd, const void *buf, size_t count) { + ssize_t res, sum = 0; + + for (;;) { + res = write(fd, buf, count); + if (res >= 0) { + sum += res; + /* do not try again if res == 0 */ + if (res == 0 || (size_t) res == count) return sum; + count -= res; + buf = (const char*) buf + res; + continue; + } + switch (errno) { + case EINTR: + continue; + default: + return -1; + } + } +} + +/* this assumes we get enough data on a successful read */ +static ssize_t safe_read(int fd, void *buf, size_t count) { + ssize_t res; + + for (;;) { + res = read(fd, buf, count); + if (res >= 0) return res; + switch (errno) { + case EINTR: + continue; + default: + return -1; + } + } +} + static int mod_rrdtool_create_rrd(server *srv, plugin_data *p, plugin_config *s) { struct stat st; - + int r; + /* check if DB already exists */ if (0 == stat(s->path_rrd->ptr, &st)) { /* check if it is plain file */ if (!S_ISREG(st.st_mode)) { - log_error_write(srv, __FILE__, __LINE__, "sb", + log_error_write(srv, __FILE__, __LINE__, "sb", "not a regular file:", s->path_rrd); return HANDLER_ERROR; } - } else { - int r ; - /* create a new one */ - - BUFFER_COPY_STRING_CONST(p->cmd, "create "); - buffer_append_string_buffer(p->cmd, s->path_rrd); - buffer_append_string(p->cmd, " --step 60 "); - buffer_append_string(p->cmd, "DS:InOctets:ABSOLUTE:600:U:U "); - buffer_append_string(p->cmd, "DS:OutOctets:ABSOLUTE:600:U:U "); - buffer_append_string(p->cmd, "DS:Requests:ABSOLUTE:600:U:U "); - buffer_append_string(p->cmd, "RRA:AVERAGE:0.5:1:600 "); - buffer_append_string(p->cmd, "RRA:AVERAGE:0.5:6:700 "); - buffer_append_string(p->cmd, "RRA:AVERAGE:0.5:24:775 "); - buffer_append_string(p->cmd, "RRA:AVERAGE:0.5:288:797 "); - buffer_append_string(p->cmd, "RRA:MAX:0.5:1:600 "); - buffer_append_string(p->cmd, "RRA:MAX:0.5:6:700 "); - buffer_append_string(p->cmd, "RRA:MAX:0.5:24:775 "); - buffer_append_string(p->cmd, "RRA:MAX:0.5:288:797 "); - buffer_append_string(p->cmd, "RRA:MIN:0.5:1:600 "); - buffer_append_string(p->cmd, "RRA:MIN:0.5:6:700 "); - buffer_append_string(p->cmd, "RRA:MIN:0.5:24:775 "); - buffer_append_string(p->cmd, "RRA:MIN:0.5:288:797\n"); - - if (-1 == (r = write(p->write_fd, p->cmd->ptr, p->cmd->used - 1))) { - log_error_write(srv, __FILE__, __LINE__, "ss", - "rrdtool-write: failed", strerror(errno)); - - return HANDLER_ERROR; - } - - buffer_prepare_copy(p->resp, 4096); - if (-1 == (r = read(p->read_fd, p->resp->ptr, p->resp->size))) { - log_error_write(srv, __FILE__, __LINE__, "ss", - "rrdtool-read: failed", strerror(errno)); - - return HANDLER_ERROR; - } - - p->resp->used = r; - - if (p->resp->ptr[0] != 'O' || - p->resp->ptr[1] != 'K') { - log_error_write(srv, __FILE__, __LINE__, "sbb", - "rrdtool-response:", p->cmd, p->resp); - - return HANDLER_ERROR; + + /* still create DB if it's empty file */ + if (st.st_size > 0) { + return HANDLER_GO_ON; } } - + + /* create a new one */ + buffer_copy_string_len(p->cmd, CONST_STR_LEN("create ")); + buffer_append_string_buffer(p->cmd, s->path_rrd); + buffer_append_string_len(p->cmd, CONST_STR_LEN( + " --step 60 " + "DS:InOctets:ABSOLUTE:600:U:U " + "DS:OutOctets:ABSOLUTE:600:U:U " + "DS:Requests:ABSOLUTE:600:U:U " + "RRA:AVERAGE:0.5:1:600 " + "RRA:AVERAGE:0.5:6:700 " + "RRA:AVERAGE:0.5:24:775 " + "RRA:AVERAGE:0.5:288:797 " + "RRA:MAX:0.5:1:600 " + "RRA:MAX:0.5:6:700 " + "RRA:MAX:0.5:24:775 " + "RRA:MAX:0.5:288:797 " + "RRA:MIN:0.5:1:600 " + "RRA:MIN:0.5:6:700 " + "RRA:MIN:0.5:24:775 " + "RRA:MIN:0.5:288:797\n")); + + if (-1 == (safe_write(p->write_fd, p->cmd->ptr, p->cmd->used - 1))) { + log_error_write(srv, __FILE__, __LINE__, "ss", + "rrdtool-write: failed", strerror(errno)); + + return HANDLER_ERROR; + } + + buffer_prepare_copy(p->resp, 4096); + if (-1 == (r = safe_read(p->read_fd, p->resp->ptr, p->resp->size))) { + log_error_write(srv, __FILE__, __LINE__, "ss", + "rrdtool-read: failed", strerror(errno)); + + return HANDLER_ERROR; + } + + p->resp->used = r; + + if (p->resp->ptr[0] != 'O' || + p->resp->ptr[1] != 'K') { + log_error_write(srv, __FILE__, __LINE__, "sbb", + "rrdtool-response:", p->cmd, p->resp); + + return HANDLER_ERROR; + } + return HANDLER_GO_ON; } @@ -255,37 +298,37 @@ static int mod_rrdtool_create_rrd(server *srv, plugin_data *p, plugin_config *s) static int mod_rrd_patch_connection(server *srv, connection *con, plugin_data *p) { size_t i, j; plugin_config *s = p->config_storage[0]; - + PATCH(path_rrdtool_bin); PATCH(path_rrd); - + p->conf.bytes_written_ptr = &(s->bytes_written); p->conf.bytes_read_ptr = &(s->bytes_read); p->conf.requests_ptr = &(s->requests); - + /* skip the first, the global context */ for (i = 1; i < srv->config_context->used; i++) { data_config *dc = (data_config *)srv->config_context->data[i]; s = p->config_storage[i]; - + /* condition didn't match */ if (!config_check_cond(srv, con, dc)) continue; - + /* merge config */ for (j = 0; j < dc->value->used; j++) { data_unset *du = dc->value->data[j]; - + if (buffer_is_equal_string(du->key, CONST_STR_LEN("rrdtool.db-name"))) { PATCH(path_rrd); /* get pointers to double values */ - + p->conf.bytes_written_ptr = &(s->bytes_written); p->conf.bytes_read_ptr = &(s->bytes_read); p->conf.requests_ptr = &(s->requests); } } } - + return 0; } #undef PATCH @@ -293,157 +336,161 @@ static int mod_rrd_patch_connection(server *srv, connection *con, plugin_data *p SETDEFAULTS_FUNC(mod_rrd_set_defaults) { plugin_data *p = p_d; size_t i; - - config_values_t cv[] = { + + config_values_t cv[] = { { "rrdtool.binary", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_SERVER }, { "rrdtool.db-name", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET } }; - + if (!p) return HANDLER_ERROR; - + p->config_storage = calloc(1, srv->config_context->used * sizeof(specific_config *)); - + for (i = 0; i < srv->config_context->used; i++) { plugin_config *s; - + s = calloc(1, sizeof(plugin_config)); s->path_rrdtool_bin = buffer_init(); s->path_rrd = buffer_init(); s->requests = 0; s->bytes_written = 0; s->bytes_read = 0; - + cv[0].destination = s->path_rrdtool_bin; cv[1].destination = s->path_rrd; - + p->config_storage[i] = s; - + if (0 != config_insert_values_global(srv, ((data_config *)srv->config_context->data[i])->value, cv)) { return HANDLER_ERROR; } - + if (i > 0 && !buffer_is_empty(s->path_rrdtool_bin)) { /* path_rrdtool_bin is a global option */ - - log_error_write(srv, __FILE__, __LINE__, "s", + + log_error_write(srv, __FILE__, __LINE__, "s", "rrdtool.binary can only be set as a global option."); - + return HANDLER_ERROR; } - + } - + p->conf.path_rrdtool_bin = p->config_storage[0]->path_rrdtool_bin; p->rrdtool_running = 0; - + /* check for dir */ - + if (buffer_is_empty(p->conf.path_rrdtool_bin)) { - log_error_write(srv, __FILE__, __LINE__, "s", + log_error_write(srv, __FILE__, __LINE__, "s", "rrdtool.binary has to be set"); return HANDLER_ERROR; } - + /* open the pipe to rrdtool */ if (mod_rrd_create_pipe(srv, p)) { return HANDLER_ERROR; } - + p->rrdtool_running = 1; - + return HANDLER_GO_ON; } TRIGGER_FUNC(mod_rrd_trigger) { plugin_data *p = p_d; size_t i; - + if (!p->rrdtool_running) return HANDLER_GO_ON; if ((srv->cur_ts % 60) != 0) return HANDLER_GO_ON; - + for (i = 0; i < srv->config_context->used; i++) { plugin_config *s = p->config_storage[i]; int r; - + if (buffer_is_empty(s->path_rrd)) continue; - + /* write the data down every minute */ - + if (HANDLER_GO_ON != mod_rrdtool_create_rrd(srv, p, s)) return HANDLER_ERROR; - - BUFFER_COPY_STRING_CONST(p->cmd, "update "); + + buffer_copy_string_len(p->cmd, CONST_STR_LEN("update ")); buffer_append_string_buffer(p->cmd, s->path_rrd); - BUFFER_APPEND_STRING_CONST(p->cmd, " N:"); + buffer_append_string_len(p->cmd, CONST_STR_LEN(" N:")); buffer_append_off_t(p->cmd, s->bytes_read); - BUFFER_APPEND_STRING_CONST(p->cmd, ":"); + buffer_append_string_len(p->cmd, CONST_STR_LEN(":")); buffer_append_off_t(p->cmd, s->bytes_written); - BUFFER_APPEND_STRING_CONST(p->cmd, ":"); + buffer_append_string_len(p->cmd, CONST_STR_LEN(":")); buffer_append_long(p->cmd, s->requests); - BUFFER_APPEND_STRING_CONST(p->cmd, "\n"); - - if (-1 == (r = write(p->write_fd, p->cmd->ptr, p->cmd->used - 1))) { + buffer_append_string_len(p->cmd, CONST_STR_LEN("\n")); + + if (-1 == (r = safe_write(p->write_fd, p->cmd->ptr, p->cmd->used - 1))) { p->rrdtool_running = 0; - - log_error_write(srv, __FILE__, __LINE__, "ss", + + log_error_write(srv, __FILE__, __LINE__, "ss", "rrdtool-write: failed", strerror(errno)); - + return HANDLER_ERROR; } - + buffer_prepare_copy(p->resp, 4096); - if (-1 == (r = read(p->read_fd, p->resp->ptr, p->resp->size))) { + if (-1 == (r = safe_read(p->read_fd, p->resp->ptr, p->resp->size))) { p->rrdtool_running = 0; - - log_error_write(srv, __FILE__, __LINE__, "ss", + + log_error_write(srv, __FILE__, __LINE__, "ss", "rrdtool-read: failed", strerror(errno)); - + return HANDLER_ERROR; } - + p->resp->used = r; - + if (p->resp->ptr[0] != 'O' || p->resp->ptr[1] != 'K') { - p->rrdtool_running = 0; - - log_error_write(srv, __FILE__, __LINE__, "sbb", + /* don't fail on this error if we just started (graceful restart, the old one might have just updated too) */ + if (!(strstr(p->resp->ptr, "(minimum one second step)") && (srv->cur_ts - srv->startup_ts < 3))) { + p->rrdtool_running = 0; + + log_error_write(srv, __FILE__, __LINE__, "sbb", "rrdtool-response:", p->cmd, p->resp); - - return HANDLER_ERROR; + + return HANDLER_ERROR; + } } s->requests = 0; s->bytes_written = 0; s->bytes_read = 0; } - + return HANDLER_GO_ON; } REQUESTDONE_FUNC(mod_rrd_account) { plugin_data *p = p_d; - + mod_rrd_patch_connection(srv, con, p); - + *(p->conf.requests_ptr) += 1; *(p->conf.bytes_written_ptr) += con->bytes_written; *(p->conf.bytes_read_ptr) += con->bytes_read; - + return HANDLER_GO_ON; } +int mod_rrdtool_plugin_init(plugin *p); int mod_rrdtool_plugin_init(plugin *p) { p->version = LIGHTTPD_VERSION_ID; p->name = buffer_init_string("rrd"); - + p->init = mod_rrd_init; p->cleanup = mod_rrd_free; p->set_defaults= mod_rrd_set_defaults; - + p->handle_trigger = mod_rrd_trigger; p->handle_request_done = mod_rrd_account; - + p->data = NULL; - + return 0; } diff --git a/src/mod_scgi.c b/src/mod_scgi.c index a27b28a..6ae0782 100644 --- a/src/mod_scgi.c +++ b/src/mod_scgi.c @@ -1,13 +1,3 @@ -#include <sys/types.h> -#include <unistd.h> -#include <errno.h> -#include <fcntl.h> -#include <string.h> -#include <stdlib.h> -#include <ctype.h> -#include <assert.h> -#include <signal.h> - #include "buffer.h" #include "server.h" #include "keyvalue.h" @@ -23,6 +13,16 @@ #include "inet_ntop_cache.h" +#include <sys/types.h> +#include <unistd.h> +#include <errno.h> +#include <fcntl.h> +#include <string.h> +#include <stdlib.h> +#include <ctype.h> +#include <assert.h> +#include <signal.h> + #include <stdio.h> #ifdef HAVE_SYS_FILIO_H @@ -31,34 +31,32 @@ #include "sys-socket.h" - -#ifndef UNIX_PATH_MAX -# define UNIX_PATH_MAX 108 -#endif - #ifdef HAVE_SYS_UIO_H -#include <sys/uio.h> +# include <sys/uio.h> #endif + #ifdef HAVE_SYS_WAIT_H -#include <sys/wait.h> +# include <sys/wait.h> #endif +#include "version.h" + enum {EOL_UNSET, EOL_N, EOL_RN}; /* - * + * * TODO: - * + * * - add timeout for a connect to a non-scgi process * (use state_timestamp + state) - * + * */ typedef struct scgi_proc { size_t id; /* id will be between 1 and max_procs */ buffer *socket; /* config.socket + "-" + id */ unsigned port; /* config.port + pno */ - + pid_t pid; /* PID of the spawned process (0 if not spawned locally) */ @@ -67,9 +65,9 @@ typedef struct scgi_proc { time_t last_used; /* see idle_timeout */ size_t requests; /* see max_requests */ struct scgi_proc *prev, *next; /* see first */ - + time_t disable_ts; /* replace by host->something */ - + int is_local; enum { PROC_STATE_UNSET, /* init-phase */ @@ -78,7 +76,7 @@ typedef struct scgi_proc { PROC_STATE_KILLED, /* was killed as we don't have the load anymore */ PROC_STATE_DIED, /* marked as dead, should be restarted */ PROC_STATE_DISABLED /* proc disabled as it resulted in an error */ - } state; + } state; } scgi_proc; typedef struct { @@ -86,20 +84,20 @@ typedef struct { * sorted by lowest load * * whenever a job is done move it up in the list - * until it is sorted, move it down as soon as the + * until it is sorted, move it down as soon as the * job is started */ - scgi_proc *first; - scgi_proc *unused_procs; + scgi_proc *first; + scgi_proc *unused_procs; - /* + /* * spawn at least min_procs, at max_procs. * - * as soon as the load of the first entry + * as soon as the load of the first entry * is max_load_per_proc we spawn a new one - * and add it to the first entry and give it + * and add it to the first entry and give it * the load - * + * */ unsigned short min_procs; @@ -111,44 +109,44 @@ typedef struct { /* * kick the process from the list if it was not - * used for idle_timeout until min_procs is + * used for idle_timeout until min_procs is * reached. this helps to get the processlist * small again we had a small peak load. * */ - + unsigned short idle_timeout; - + /* * time after a disabled remote connection is tried to be re-enabled - * - * + * + * */ - + unsigned short disable_time; /* * same scgi processes get a little bit larger - * than wanted. max_requests_per_proc kills a + * than wanted. max_requests_per_proc kills a * process after a number of handled requests. * */ size_t max_requests_per_proc; - + /* config */ - /* - * host:port + /* + * host:port * - * if host is one of the local IP adresses the + * if host is one of the local IP adresses the * whole connection is local * * if tcp/ip should be used host AND port have - * to be specified - * - */ - buffer *host; + * to be specified + * + */ + buffer *host; unsigned short port; /* @@ -161,7 +159,7 @@ typedef struct { */ buffer *unixsocket; - /* if socket is local we can start the scgi + /* if socket is local we can start the scgi * process ourself * * bin-path is the path to the binary @@ -169,19 +167,19 @@ typedef struct { * check min_procs and max_procs for the number * of process to start-up */ - buffer *bin_path; - - /* bin-path is set bin-environment is taken to + buffer *bin_path; + + /* bin-path is set bin-environment is taken to * create the environement before starting the * FastCGI process - * + * */ array *bin_env; - + array *bin_env_copy; - + /* - * docroot-translation between URL->phys and the + * docroot-translation between URL->phys and the * remote host * * reasons: @@ -192,7 +190,7 @@ typedef struct { buffer *docroot; /* - * check_local tell you if the phys file is stat()ed + * check_local tell you if the phys file is stat()ed * or not. FastCGI doesn't care if the service is * remote. If the web-server side doesn't contain * the scgi-files we should not stat() for them @@ -202,33 +200,42 @@ typedef struct { /* * append PATH_INFO to SCRIPT_FILENAME - * + * * php needs this if cgi.fix_pathinfo is provied - * + * */ - + + /* + * workaround for program when prefix="/" + * + * rule to build PATH_INFO is hardcoded for when check_local is disabled + * enable this option to use the workaround + * + */ + + unsigned short fix_root_path_name; ssize_t load; /* replace by host->load */ size_t max_id; /* corresponds most of the time to num_procs. - + only if a process is killed max_id waits for the process itself to die and decrements its afterwards */ } scgi_extension_host; /* * one extension can have multiple hosts assigned - * one host can spawn additional processes on the same + * one host can spawn additional processes on the same * socket (if we control it) * * ext -> host -> procs * 1:n 1:n * - * if the scgi process is remote that whole goes down + * if the scgi process is remote that whole goes down * to * * ext -> host -> procs - * 1:n 1:1 + * 1:n 1:1 * * in case of PHP and FCGI_CHILDREN we have again a procs * but we don't control it directly. @@ -238,8 +245,9 @@ typedef struct { typedef struct { buffer *key; /* like .php */ + int note_is_sent; scgi_extension_host **hosts; - + size_t used; size_t size; } scgi_extension; @@ -253,14 +261,14 @@ typedef struct { typedef struct { - scgi_exts *exts; - + scgi_exts *exts; + int debug; } plugin_config; typedef struct { char **ptr; - + size_t size; size_t used; } char_array; @@ -268,88 +276,101 @@ typedef struct { /* generic plugin data, shared between all connections */ typedef struct { PLUGIN_DATA; - + buffer *scgi_env; - + buffer *path; buffer *parse_response; - + plugin_config **config_storage; - + plugin_config conf; /* this is only used as long as no handler_ctx is setup */ } plugin_data; /* connection specific data */ -typedef enum { FCGI_STATE_INIT, FCGI_STATE_CONNECT, FCGI_STATE_PREPARE_WRITE, - FCGI_STATE_WRITE, FCGI_STATE_READ +typedef enum { FCGI_STATE_INIT, FCGI_STATE_CONNECT, FCGI_STATE_PREPARE_WRITE, + FCGI_STATE_WRITE, FCGI_STATE_READ } scgi_connection_state_t; typedef struct { - buffer *response; + buffer *response; size_t response_len; int response_type; int response_padding; - + scgi_proc *proc; scgi_extension_host *host; - + scgi_connection_state_t state; time_t state_timestamp; - + int reconnects; /* number of reconnect attempts */ - + read_buffer *rb; chunkqueue *wb; - + buffer *response_header; - + int delayed; /* flag to mark that the connect() is delayed */ - + size_t request_id; int fd; /* fd to the scgi process */ int fde_ndx; /* index into the fd-event buffer */ pid_t pid; int got_proc; - + plugin_config conf; - + connection *remote_conn; /* dumb pointer */ plugin_data *plugin_data; /* dumb pointer */ } handler_ctx; /* ok, we need a prototype */ -static handler_t scgi_handle_fdevent(void *s, void *ctx, int revents); +static handler_t scgi_handle_fdevent(server *srv, void *ctx, int revents); int scgi_proclist_sort_down(server *srv, scgi_extension_host *host, scgi_proc *proc); +static void reset_signals(void) { +#ifdef SIGTTOU + signal(SIGTTOU, SIG_DFL); +#endif +#ifdef SIGTTIN + signal(SIGTTIN, SIG_DFL); +#endif +#ifdef SIGTSTP + signal(SIGTSTP, SIG_DFL); +#endif + signal(SIGHUP, SIG_DFL); + signal(SIGPIPE, SIG_DFL); + signal(SIGUSR1, SIG_DFL); +} - -static handler_ctx * handler_ctx_init() { +static handler_ctx * handler_ctx_init(void) { handler_ctx * hctx; - + hctx = calloc(1, sizeof(*hctx)); assert(hctx); - + hctx->fde_ndx = -1; - + hctx->response = buffer_init(); hctx->response_header = buffer_init(); - + hctx->request_id = 0; hctx->state = FCGI_STATE_INIT; hctx->proc = NULL; - + hctx->response_len = 0; hctx->response_type = 0; hctx->response_padding = 0; hctx->fd = -1; - + hctx->reconnects = 0; hctx->wb = chunkqueue_init(); - + return hctx; } @@ -358,38 +379,38 @@ static void handler_ctx_free(handler_ctx *hctx) { buffer_free(hctx->response_header); chunkqueue_free(hctx->wb); - + if (hctx->rb) { if (hctx->rb->ptr) free(hctx->rb->ptr); free(hctx->rb); } - + free(hctx); } -scgi_proc *scgi_process_init() { +static scgi_proc *scgi_process_init(void) { scgi_proc *f; f = calloc(1, sizeof(*f)); f->socket = buffer_init(); - + f->prev = NULL; f->next = NULL; - + return f; } -void scgi_process_free(scgi_proc *f) { +static void scgi_process_free(scgi_proc *f) { if (!f) return; - + scgi_process_free(f->next); - + buffer_free(f->socket); - + free(f); } -scgi_extension_host *scgi_host_init() { +static scgi_extension_host *scgi_host_init(void) { scgi_extension_host *f; f = calloc(1, sizeof(*f)); @@ -400,66 +421,66 @@ scgi_extension_host *scgi_host_init() { f->bin_path = buffer_init(); f->bin_env = array_init(); f->bin_env_copy = array_init(); - + return f; } -void scgi_host_free(scgi_extension_host *h) { +static void scgi_host_free(scgi_extension_host *h) { if (!h) return; - + buffer_free(h->host); buffer_free(h->unixsocket); buffer_free(h->docroot); buffer_free(h->bin_path); array_free(h->bin_env); array_free(h->bin_env_copy); - + scgi_process_free(h->first); scgi_process_free(h->unused_procs); - + free(h); - + } -scgi_exts *scgi_extensions_init() { +static scgi_exts *scgi_extensions_init(void) { scgi_exts *f; f = calloc(1, sizeof(*f)); - + return f; } -void scgi_extensions_free(scgi_exts *f) { +static void scgi_extensions_free(scgi_exts *f) { size_t i; - + if (!f) return; - + for (i = 0; i < f->used; i++) { scgi_extension *fe; size_t j; - + fe = f->exts[i]; - + for (j = 0; j < fe->used; j++) { scgi_extension_host *h; - + h = fe->hosts[j]; - + scgi_host_free(h); } - + buffer_free(fe->key); free(fe->hosts); - + free(fe); } - + free(f->exts); - + free(f); } -int scgi_extension_insert(scgi_exts *ext, buffer *key, scgi_extension_host *fh) { +static int scgi_extension_insert(scgi_exts *ext, buffer *key, scgi_extension_host *fh) { scgi_extension *fe; size_t i; @@ -504,99 +525,109 @@ int scgi_extension_insert(scgi_exts *ext, buffer *key, scgi_extension_host *fh) assert(fe->hosts); } - fe->hosts[fe->used++] = fh; + fe->hosts[fe->used++] = fh; return 0; - + } INIT_FUNC(mod_scgi_init) { plugin_data *p; - + p = calloc(1, sizeof(*p)); - + p->scgi_env = buffer_init(); - + p->path = buffer_init(); p->parse_response = buffer_init(); - + return p; } FREE_FUNC(mod_scgi_free) { plugin_data *p = p_d; - + UNUSED(srv); buffer_free(p->scgi_env); buffer_free(p->path); buffer_free(p->parse_response); - + if (p->config_storage) { size_t i, j, n; for (i = 0; i < srv->config_context->used; i++) { plugin_config *s = p->config_storage[i]; scgi_exts *exts; - + if (!s) continue; - + exts = s->exts; for (j = 0; j < exts->used; j++) { scgi_extension *ex; - + ex = exts->exts[j]; - + for (n = 0; n < ex->used; n++) { scgi_proc *proc; scgi_extension_host *host; - + host = ex->hosts[n]; - + for (proc = host->first; proc; proc = proc->next) { if (proc->pid != 0) kill(proc->pid, SIGTERM); - - if (proc->is_local && + + if (proc->is_local && !buffer_is_empty(proc->socket)) { unlink(proc->socket->ptr); } } - + for (proc = host->unused_procs; proc; proc = proc->next) { if (proc->pid != 0) kill(proc->pid, SIGTERM); - - if (proc->is_local && + + if (proc->is_local && !buffer_is_empty(proc->socket)) { unlink(proc->socket->ptr); } } } } - + scgi_extensions_free(s->exts); - + free(s); } free(p->config_storage); } - + free(p); - + return HANDLER_GO_ON; } static int env_add(char_array *env, const char *key, size_t key_len, const char *val, size_t val_len) { char *dst; - + size_t i; + if (!key || !val) return -1; - + dst = malloc(key_len + val_len + 3); memcpy(dst, key, key_len); dst[key_len] = '='; /* add the \0 from the value */ memcpy(dst + key_len + 1, val, val_len + 1); - + + for (i = 0; i < env->used; i++) { + if (0 == strncmp(dst, env->ptr[i], key_len + 1)) { + /* don't care about free as we are in a forked child which is going to exec(...) */ + /* free(env->ptr[i]); */ + env->ptr[i] = dst; + return 0; + } + } + if (env->size == 0) { env->size = 16; env->ptr = malloc(env->size * sizeof(*env->ptr)); @@ -604,13 +635,13 @@ static int env_add(char_array *env, const char *key, size_t key_len, const char env->size += 16; env->ptr = realloc(env->ptr, env->size * sizeof(*env->ptr)); } - + env->ptr[env->used++] = dst; - + return 0; } -static int scgi_spawn_connection(server *srv, +static int scgi_spawn_connection(server *srv, plugin_data *p, scgi_extension_host *host, scgi_proc *proc) { @@ -622,30 +653,30 @@ static int scgi_spawn_connection(server *srv, #endif struct sockaddr_in scgi_addr_in; struct sockaddr *scgi_addr; - + socklen_t servlen; - + #ifndef HAVE_FORK return -1; #endif - + if (p->conf.debug) { log_error_write(srv, __FILE__, __LINE__, "sdb", "new proc, socket:", proc->port, proc->socket); } - + if (!buffer_is_empty(proc->socket)) { memset(&scgi_addr, 0, sizeof(scgi_addr)); - + #ifdef HAVE_SYS_UN_H scgi_addr_un.sun_family = AF_UNIX; strcpy(scgi_addr_un.sun_path, proc->socket->ptr); - + #ifdef SUN_LEN servlen = SUN_LEN(&scgi_addr_un); #else /* stevens says: */ - servlen = proc->socket->used - 1 + sizeof(scgi_addr_un.sun_family); + servlen = proc->socket->used + sizeof(scgi_addr_un.sun_family); #endif socket_type = AF_UNIX; scgi_addr = (struct sockaddr *) &scgi_addr_un; @@ -656,115 +687,120 @@ static int scgi_spawn_connection(server *srv, #endif } else { scgi_addr_in.sin_family = AF_INET; - + if (buffer_is_empty(host->host)) { scgi_addr_in.sin_addr.s_addr = htonl(INADDR_ANY); } else { struct hostent *he; - + /* set a usefull default */ scgi_addr_in.sin_addr.s_addr = htonl(INADDR_ANY); - - + + if (NULL == (he = gethostbyname(host->host->ptr))) { - log_error_write(srv, __FILE__, __LINE__, - "sdb", "gethostbyname failed: ", + log_error_write(srv, __FILE__, __LINE__, + "sdb", "gethostbyname failed: ", h_errno, host->host); return -1; } - + if (he->h_addrtype != AF_INET) { log_error_write(srv, __FILE__, __LINE__, "sd", "addr-type != AF_INET: ", he->h_addrtype); return -1; } - + if (he->h_length != sizeof(struct in_addr)) { log_error_write(srv, __FILE__, __LINE__, "sd", "addr-length != sizeof(in_addr): ", he->h_length); return -1; } - + memcpy(&(scgi_addr_in.sin_addr.s_addr), he->h_addr_list[0], he->h_length); - + } scgi_addr_in.sin_port = htons(proc->port); servlen = sizeof(scgi_addr_in); - + socket_type = AF_INET; scgi_addr = (struct sockaddr *) &scgi_addr_in; } - + if (-1 == (scgi_fd = socket(socket_type, SOCK_STREAM, 0))) { - log_error_write(srv, __FILE__, __LINE__, "ss", + log_error_write(srv, __FILE__, __LINE__, "ss", "failed:", strerror(errno)); return -1; } - + if (-1 == connect(scgi_fd, scgi_addr, servlen)) { /* server is not up, spawn in */ pid_t child; int val; - + if (!buffer_is_empty(proc->socket)) { unlink(proc->socket->ptr); } - + close(scgi_fd); - + /* reopen socket */ if (-1 == (scgi_fd = socket(socket_type, SOCK_STREAM, 0))) { - log_error_write(srv, __FILE__, __LINE__, "ss", + log_error_write(srv, __FILE__, __LINE__, "ss", "socket failed:", strerror(errno)); return -1; } - + val = 1; if (setsockopt(scgi_fd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val)) < 0) { - log_error_write(srv, __FILE__, __LINE__, "ss", + log_error_write(srv, __FILE__, __LINE__, "ss", "socketsockopt failed:", strerror(errno)); return -1; } - + /* create socket */ if (-1 == bind(scgi_fd, scgi_addr, servlen)) { - log_error_write(srv, __FILE__, __LINE__, "sbds", - "bind failed for:", - proc->socket, - proc->port, + log_error_write(srv, __FILE__, __LINE__, "sbds", + "bind failed for:", + proc->socket, + proc->port, strerror(errno)); return -1; } - + if (-1 == listen(scgi_fd, 1024)) { - log_error_write(srv, __FILE__, __LINE__, "ss", + log_error_write(srv, __FILE__, __LINE__, "ss", "listen failed:", strerror(errno)); return -1; } - -#ifdef HAVE_FORK + +#ifdef HAVE_FORK switch ((child = fork())) { case 0: { buffer *b; size_t i = 0; int fd = 0; char_array env; - - + + /* create environment */ env.ptr = NULL; env.size = 0; env.used = 0; - + + if (scgi_fd != 0) { + dup2(scgi_fd, 0); + close(scgi_fd); + } + /* we don't need the client socket */ for (fd = 3; fd < 256; fd++) { - if (fd != 2 && fd != scgi_fd) close(fd); + close(fd); } - + /* build clean environment */ if (host->bin_env_copy->used) { for (i = 0; i < host->bin_env_copy->used; i++) { data_string *ds = (data_string *)host->bin_env_copy->data[i]; char *ge; - + if (NULL != (ge = getenv(ds->value->ptr))) { env_add(&env, CONST_BUF_LEN(ds->value), ge, strlen(ge)); } @@ -772,44 +808,46 @@ static int scgi_spawn_connection(server *srv, } else { for (i = 0; environ[i]; i++) { char *eq; - + if (NULL != (eq = strchr(environ[i], '='))) { env_add(&env, environ[i], eq - environ[i], eq+1, strlen(eq+1)); } } } - + /* create environment */ for (i = 0; i < host->bin_env->used; i++) { data_string *ds = (data_string *)host->bin_env->data[i]; - + env_add(&env, CONST_BUF_LEN(ds->key), CONST_BUF_LEN(ds->value)); } - + for (i = 0; i < env.used; i++) { /* search for PHP_FCGI_CHILDREN */ if (0 == strncmp(env.ptr[i], "PHP_FCGI_CHILDREN=", sizeof("PHP_FCGI_CHILDREN=") - 1)) break; } - + /* not found, add a default */ if (i == env.used) { env_add(&env, CONST_STR_LEN("PHP_FCGI_CHILDREN"), CONST_STR_LEN("1")); } - + env.ptr[env.used] = NULL; - + b = buffer_init(); - buffer_copy_string(b, "exec "); + buffer_copy_string_len(b, CONST_STR_LEN("exec ")); buffer_append_string_buffer(b, host->bin_path); - + + reset_signals(); + /* exec the cgi */ - execle("/bin/sh", "sh", "-c", b->ptr, NULL, env.ptr); - - log_error_write(srv, __FILE__, __LINE__, "sbs", + execle("/bin/sh", "sh", "-c", b->ptr, (char *)NULL, env.ptr); + + log_error_write(srv, __FILE__, __LINE__, "sbs", "execl failed for:", host->bin_path, strerror(errno)); - + exit(errno); - + break; } case -1: @@ -817,32 +855,32 @@ static int scgi_spawn_connection(server *srv, break; default: /* father */ - + /* wait */ select(0, NULL, NULL, NULL, &tv); - + switch (waitpid(child, &status, WNOHANG)) { case 0: /* child still running after timeout, good */ break; case -1: /* no PID found ? should never happen */ - log_error_write(srv, __FILE__, __LINE__, "ss", + log_error_write(srv, __FILE__, __LINE__, "ss", "pid not found:", strerror(errno)); return -1; default: /* the child should not terminate at all */ if (WIFEXITED(status)) { - log_error_write(srv, __FILE__, __LINE__, "sd", - "child exited (is this a SCGI binary ?):", + log_error_write(srv, __FILE__, __LINE__, "sd", + "child exited (is this a SCGI binary ?):", WEXITSTATUS(status)); } else if (WIFSIGNALED(status)) { - log_error_write(srv, __FILE__, __LINE__, "sd", - "child signaled:", + log_error_write(srv, __FILE__, __LINE__, "sd", + "child signaled:", WTERMSIG(status)); } else { - log_error_write(srv, __FILE__, __LINE__, "sd", - "child died somehow:", + log_error_write(srv, __FILE__, __LINE__, "sd", + "child died somehow:", status); } return -1; @@ -852,26 +890,26 @@ static int scgi_spawn_connection(server *srv, proc->pid = child; proc->last_used = srv->cur_ts; proc->is_local = 1; - + break; } #endif } else { proc->is_local = 0; proc->pid = 0; - + if (p->conf.debug) { log_error_write(srv, __FILE__, __LINE__, "sb", "(debug) socket is already used, won't spawn:", proc->socket); } } - + proc->state = PROC_STATE_RUNNING; host->active_procs++; - + close(scgi_fd); - + return 0; } @@ -880,89 +918,89 @@ SETDEFAULTS_FUNC(mod_scgi_set_defaults) { plugin_data *p = p_d; data_unset *du; size_t i = 0; - - config_values_t cv[] = { + + config_values_t cv[] = { { "scgi.server", NULL, T_CONFIG_LOCAL, T_CONFIG_SCOPE_CONNECTION }, /* 0 */ { "scgi.debug", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 1 */ { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET } }; - + p->config_storage = calloc(1, srv->config_context->used * sizeof(specific_config *)); - + for (i = 0; i < srv->config_context->used; i++) { plugin_config *s; array *ca; - + s = malloc(sizeof(plugin_config)); s->exts = scgi_extensions_init(); s->debug = 0; - + cv[0].destination = s->exts; cv[1].destination = &(s->debug); - + p->config_storage[i] = s; ca = ((data_config *)srv->config_context->data[i])->value; - + if (0 != config_insert_values_global(srv, ca, cv)) { return HANDLER_ERROR; } - - /* + + /* * <key> = ( ... ) */ - + if (NULL != (du = array_get_element(ca, "scgi.server"))) { size_t j; data_array *da = (data_array *)du; - + if (du->type != TYPE_ARRAY) { - log_error_write(srv, __FILE__, __LINE__, "sss", + log_error_write(srv, __FILE__, __LINE__, "sss", "unexpected type for key: ", "scgi.server", "array of strings"); - + return HANDLER_ERROR; } - - - /* - * scgi.server = ( "<ext>" => ( ... ), + + + /* + * scgi.server = ( "<ext>" => ( ... ), * "<ext>" => ( ... ) ) */ - + for (j = 0; j < da->value->used; j++) { size_t n; data_array *da_ext = (data_array *)da->value->data[j]; - + if (da->value->data[j]->type != TYPE_ARRAY) { - log_error_write(srv, __FILE__, __LINE__, "sssbs", - "unexpected type for key: ", "scgi.server", + log_error_write(srv, __FILE__, __LINE__, "sssbs", + "unexpected type for key: ", "scgi.server", "[", da->value->data[j]->key, "](string)"); - + return HANDLER_ERROR; } - - /* - * da_ext->key == name of the extension + + /* + * da_ext->key == name of the extension */ - - /* - * scgi.server = ( "<ext>" => - * ( "<host>" => ( ... ), + + /* + * scgi.server = ( "<ext>" => + * ( "<host>" => ( ... ), * "<host>" => ( ... ) - * ), + * ), * "<ext>" => ... ) */ - + for (n = 0; n < da_ext->value->used; n++) { data_array *da_host = (data_array *)da_ext->value->data[n]; - + scgi_extension_host *df; - - config_values_t fcv[] = { + + config_values_t fcv[] = { { "host", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 0 */ { "docroot", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 1 */ { "socket", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 2 */ { "bin-path", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 3 */ - + { "check-local", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 4 */ { "port", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 5 */ { "min-procs-not-working", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 7 this is broken for now */ @@ -970,37 +1008,39 @@ SETDEFAULTS_FUNC(mod_scgi_set_defaults) { { "max-load-per-proc", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 8 */ { "idle-timeout", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 9 */ { "disable-time", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 10 */ - + { "bin-environment", NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION }, /* 11 */ { "bin-copy-environment", NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION }, /* 12 */ - - + { "fix-root-scriptname", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 13 */ + + { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET } }; - + if (da_host->type != TYPE_ARRAY) { - log_error_write(srv, __FILE__, __LINE__, "ssSBS", - "unexpected type for key:", - "scgi.server", + log_error_write(srv, __FILE__, __LINE__, "ssSBS", + "unexpected type for key:", + "scgi.server", "[", da_host->key, "](string)"); - + return HANDLER_ERROR; } - + df = scgi_host_init(); - + df->check_local = 1; df->min_procs = 4; df->max_procs = 4; df->max_load_per_proc = 1; df->idle_timeout = 60; df->disable_time = 60; - + df->fix_root_path_name = 0; + fcv[0].destination = df->host; fcv[1].destination = df->docroot; fcv[2].destination = df->unixsocket; fcv[3].destination = df->bin_path; - + fcv[4].destination = &(df->check_local); fcv[5].destination = &(df->port); fcv[6].destination = &(df->min_procs); @@ -1008,47 +1048,49 @@ SETDEFAULTS_FUNC(mod_scgi_set_defaults) { fcv[8].destination = &(df->max_load_per_proc); fcv[9].destination = &(df->idle_timeout); fcv[10].destination = &(df->disable_time); - + fcv[11].destination = df->bin_env; fcv[12].destination = df->bin_env_copy; - - + fcv[13].destination = &(df->fix_root_path_name); + + if (0 != config_insert_values_internal(srv, da_host->value, fcv)) { return HANDLER_ERROR; } - - if ((!buffer_is_empty(df->host) || df->port) && + + if ((!buffer_is_empty(df->host) || df->port) && !buffer_is_empty(df->unixsocket)) { - log_error_write(srv, __FILE__, __LINE__, "s", + log_error_write(srv, __FILE__, __LINE__, "s", "either host+port or socket"); - + return HANDLER_ERROR; } - + if (!buffer_is_empty(df->unixsocket)) { /* unix domain socket */ - - if (df->unixsocket->used > UNIX_PATH_MAX - 2) { - log_error_write(srv, __FILE__, __LINE__, "s", + struct sockaddr_un un; + + if (df->unixsocket->used > sizeof(un.sun_path) - 2) { + log_error_write(srv, __FILE__, __LINE__, "s", "path of the unixdomain socket is too large"); return HANDLER_ERROR; } } else { /* tcp/ip */ - - if (buffer_is_empty(df->host) && + + if (buffer_is_empty(df->host) && buffer_is_empty(df->bin_path)) { - log_error_write(srv, __FILE__, __LINE__, "sbbbs", - "missing key (string):", + log_error_write(srv, __FILE__, __LINE__, "sbbbs", + "missing key (string):", da->key, da_ext->key, da_host->key, "host"); - + return HANDLER_ERROR; } else if (df->port == 0) { - log_error_write(srv, __FILE__, __LINE__, "sbbbs", - "missing key (short):", + log_error_write(srv, __FILE__, __LINE__, "sbbbs", + "missing key (short):", da->key, da_ext->key, da_host->key, @@ -1056,14 +1098,17 @@ SETDEFAULTS_FUNC(mod_scgi_set_defaults) { return HANDLER_ERROR; } } - - if (!buffer_is_empty(df->bin_path)) { + + if (!buffer_is_empty(df->bin_path)) { /* a local socket + self spawning */ size_t pno; - + + /* HACK: just to make sure the adaptive spawing is disabled */ + df->min_procs = df->max_procs; + if (df->min_procs > df->max_procs) df->max_procs = df->min_procs; if (df->max_load_per_proc < 1) df->max_load_per_proc = 0; - + if (s->debug) { log_error_write(srv, __FILE__, __LINE__, "ssbsdsbsdsd", "--- scgi spawning local", @@ -1073,7 +1118,7 @@ SETDEFAULTS_FUNC(mod_scgi_set_defaults) { "\n\tmin-procs:", df->min_procs, "\n\tmax-procs:", df->max_procs); } - + for (pno = 0; pno < df->min_procs; pno++) { scgi_proc *proc; @@ -1085,10 +1130,10 @@ SETDEFAULTS_FUNC(mod_scgi_set_defaults) { proc->port = df->port + pno; } else { buffer_copy_string_buffer(proc->socket, df->unixsocket); - buffer_append_string(proc->socket, "-"); + buffer_append_string_len(proc->socket, CONST_STR_LEN("-")); buffer_append_long(proc->socket, pno); } - + if (s->debug) { log_error_write(srv, __FILE__, __LINE__, "ssdsbsdsd", "--- scgi spawning", @@ -1096,88 +1141,83 @@ SETDEFAULTS_FUNC(mod_scgi_set_defaults) { "\n\tsocket", df->unixsocket, "\n\tcurrent:", pno, "/", df->min_procs); } - + if (scgi_spawn_connection(srv, p, df, proc)) { log_error_write(srv, __FILE__, __LINE__, "s", "[ERROR]: spawning fcgi failed."); return HANDLER_ERROR; } - + proc->next = df->first; if (df->first) df->first->prev = proc; - + df->first = proc; } } else { scgi_proc *fp; - + fp = scgi_process_init(); fp->id = df->num_procs++; df->max_id++; df->active_procs++; fp->state = PROC_STATE_RUNNING; - + if (buffer_is_empty(df->unixsocket)) { fp->port = df->port; } else { buffer_copy_string_buffer(fp->socket, df->unixsocket); } - + df->first = fp; - + df->min_procs = 1; df->max_procs = 1; } - + /* if extension already exists, take it */ scgi_extension_insert(s->exts, da_ext->key, df); } } } } - + return HANDLER_GO_ON; } static int scgi_set_state(server *srv, handler_ctx *hctx, scgi_connection_state_t state) { hctx->state = state; hctx->state_timestamp = srv->cur_ts; - + return 0; } -void scgi_connection_cleanup(server *srv, handler_ctx *hctx) { +static void scgi_connection_cleanup(server *srv, handler_ctx *hctx) { plugin_data *p; connection *con; - + if (NULL == hctx) return; - + p = hctx->plugin_data; con = hctx->remote_conn; - - if (con->mode != p->id) { - WP(); - return; - } - + if (hctx->fd != -1) { fdevent_event_del(srv->ev, &(hctx->fde_ndx), hctx->fd); fdevent_unregister(srv->ev, hctx->fd); close(hctx->fd); srv->cur_fds--; } - + if (hctx->host && hctx->proc) { hctx->host->load--; - + if (hctx->got_proc) { /* after the connect the process gets a load */ hctx->proc->load--; - + if (p->conf.debug) { log_error_write(srv, __FILE__, __LINE__, "sddb", - "release proc:", + "release proc:", hctx->fd, hctx->proc->pid, hctx->proc->socket); } @@ -1186,87 +1226,88 @@ void scgi_connection_cleanup(server *srv, handler_ctx *hctx) { scgi_proclist_sort_down(srv, hctx->host, hctx->proc); } - + handler_ctx_free(hctx); - con->plugin_ctx[p->id] = NULL; + con->plugin_ctx[p->id] = NULL; } static int scgi_reconnect(server *srv, handler_ctx *hctx) { plugin_data *p = hctx->plugin_data; - - /* child died - * - * 1. - * + + /* child died + * + * 1. + * * connect was ok, connection was accepted * but the php accept loop checks after the accept if it should die or not. - * - * if yes we can only detect it at a write() - * + * + * if yes we can only detect it at a write() + * * next step is resetting this attemp and setup a connection again - * + * * if we have more then 5 reconnects for the same request, die - * - * 2. - * + * + * 2. + * * we have a connection but the child died by some other reason - * + * */ - + fdevent_event_del(srv->ev, &(hctx->fde_ndx), hctx->fd); fdevent_unregister(srv->ev, hctx->fd); close(hctx->fd); srv->cur_fds--; - + scgi_set_state(srv, hctx, FCGI_STATE_INIT); - + hctx->request_id = 0; hctx->reconnects++; - + if (p->conf.debug) { log_error_write(srv, __FILE__, __LINE__, "sddb", - "release proc:", + "release proc:", hctx->fd, hctx->proc->pid, hctx->proc->socket); } - + hctx->proc->load--; scgi_proclist_sort_down(srv, hctx->host, hctx->proc); - + return 0; } static handler_t scgi_connection_reset(server *srv, connection *con, void *p_d) { plugin_data *p = p_d; - + scgi_connection_cleanup(srv, con->plugin_ctx[p->id]); - + return HANDLER_GO_ON; } static int scgi_env_add(buffer *env, const char *key, size_t key_len, const char *val, size_t val_len) { size_t len; - + if (!key || !val) return -1; - + len = key_len + val_len + 2; - + buffer_prepare_append(env, len); - /* include the NUL */ - memcpy(env->ptr + env->used, key, key_len + 1); + memcpy(env->ptr + env->used, key, key_len); + env->ptr[env->used + key_len] = '\0'; env->used += key_len + 1; - memcpy(env->ptr + env->used, val, val_len + 1); + memcpy(env->ptr + env->used, val, val_len); + env->ptr[env->used + val_len] = '\0'; env->used += val_len + 1; - + return 0; } /** - * + * * returns * -1 error * 0 connected @@ -1280,13 +1321,13 @@ static int scgi_establish_connection(server *srv, handler_ctx *hctx) { struct sockaddr_un scgi_addr_un; #endif socklen_t servlen; - + scgi_extension_host *host = hctx->host; scgi_proc *proc = hctx->proc; int scgi_fd = hctx->fd; - + memset(&scgi_addr, 0, sizeof(scgi_addr)); - + if (!buffer_is_empty(proc->socket)) { #ifdef HAVE_SYS_UN_H /* use the unix domain socket */ @@ -1296,7 +1337,7 @@ static int scgi_establish_connection(server *srv, handler_ctx *hctx) { servlen = SUN_LEN(&scgi_addr_un); #else /* stevens says: */ - servlen = proc->socket->used - 1 + sizeof(scgi_addr_un.sun_family); + servlen = proc->socket->used + sizeof(scgi_addr_un.sun_family); #endif scgi_addr = (struct sockaddr *) &scgi_addr_un; #else @@ -1305,105 +1346,105 @@ static int scgi_establish_connection(server *srv, handler_ctx *hctx) { } else { scgi_addr_in.sin_family = AF_INET; if (0 == inet_aton(host->host->ptr, &(scgi_addr_in.sin_addr))) { - log_error_write(srv, __FILE__, __LINE__, "sbs", - "converting IP-adress failed for", host->host, + log_error_write(srv, __FILE__, __LINE__, "sbs", + "converting IP-adress failed for", host->host, "\nBe sure to specify an IP address here"); - + return -1; } scgi_addr_in.sin_port = htons(proc->port); servlen = sizeof(scgi_addr_in); - + scgi_addr = (struct sockaddr *) &scgi_addr_in; } - + if (-1 == connect(scgi_fd, scgi_addr, servlen)) { - if (errno == EINPROGRESS || + if (errno == EINPROGRESS || errno == EALREADY || errno == EINTR) { if (hctx->conf.debug) { - log_error_write(srv, __FILE__, __LINE__, "sd", + log_error_write(srv, __FILE__, __LINE__, "sd", "connect delayed, will continue later:", scgi_fd); } - + return 1; } else { - log_error_write(srv, __FILE__, __LINE__, "sdsddb", - "connect failed:", scgi_fd, + log_error_write(srv, __FILE__, __LINE__, "sdsddb", + "connect failed:", scgi_fd, strerror(errno), errno, proc->port, proc->socket); if (errno == EAGAIN) { /* this is Linux only */ - - log_error_write(srv, __FILE__, __LINE__, "s", + + log_error_write(srv, __FILE__, __LINE__, "s", "If this happend on Linux: You have been run out of local ports. " "Check the manual, section Performance how to handle this."); - } - + } + return -1; } } if (hctx->conf.debug > 1) { - log_error_write(srv, __FILE__, __LINE__, "sd", + log_error_write(srv, __FILE__, __LINE__, "sd", "connect succeeded: ", scgi_fd); } - + return 0; } static int scgi_env_add_request_headers(server *srv, connection *con, plugin_data *p) { size_t i; - + for (i = 0; i < con->request.headers->used; i++) { data_string *ds; - + ds = (data_string *)con->request.headers->data[i]; - + if (ds->value->used && ds->key->used) { size_t j; buffer_reset(srv->tmp_buf); - + if (0 != strcasecmp(ds->key->ptr, "CONTENT-TYPE")) { - BUFFER_COPY_STRING_CONST(srv->tmp_buf, "HTTP_"); + buffer_copy_string_len(srv->tmp_buf, CONST_STR_LEN("HTTP_")); srv->tmp_buf->used--; } - + buffer_prepare_append(srv->tmp_buf, ds->key->used + 2); for (j = 0; j < ds->key->used - 1; j++) { - srv->tmp_buf->ptr[srv->tmp_buf->used++] = - light_isalpha(ds->key->ptr[j]) ? + srv->tmp_buf->ptr[srv->tmp_buf->used++] = + light_isalpha(ds->key->ptr[j]) ? ds->key->ptr[j] & ~32 : '_'; } srv->tmp_buf->ptr[srv->tmp_buf->used++] = '\0'; - + scgi_env_add(p->scgi_env, CONST_BUF_LEN(srv->tmp_buf), CONST_BUF_LEN(ds->value)); } } - + for (i = 0; i < con->environment->used; i++) { data_string *ds; - + ds = (data_string *)con->environment->data[i]; - + if (ds->value->used && ds->key->used) { size_t j; buffer_reset(srv->tmp_buf); - + buffer_prepare_append(srv->tmp_buf, ds->key->used + 2); for (j = 0; j < ds->key->used - 1; j++) { - srv->tmp_buf->ptr[srv->tmp_buf->used++] = - isalpha((unsigned char)ds->key->ptr[j]) ? + srv->tmp_buf->ptr[srv->tmp_buf->used++] = + light_isalnum((unsigned char)ds->key->ptr[j]) ? toupper((unsigned char)ds->key->ptr[j]) : '_'; } srv->tmp_buf->ptr[srv->tmp_buf->used++] = '\0'; - + scgi_env_add(p->scgi_env, CONST_BUF_LEN(srv->tmp_buf), CONST_BUF_LEN(ds->value)); } } - + return 0; } @@ -1415,34 +1456,48 @@ static int scgi_create_env(server *srv, handler_ctx *hctx) { char b2[INET6_ADDRSTRLEN + 1]; #endif buffer *b; - + plugin_data *p = hctx->plugin_data; scgi_extension_host *host= hctx->host; connection *con = hctx->remote_conn; server_socket *srv_sock = con->srv_socket; - + sock_addr our_addr; socklen_t our_addr_len; - + buffer_prepare_copy(p->scgi_env, 1024); /* CGI-SPEC 6.1.2, FastCGI spec 6.3 and SCGI spec */ - + /* request.content_length < SSIZE_MAX, see request.c */ - ltostr(buf, con->request.content_length); + LI_ltostr(buf, con->request.content_length); scgi_env_add(p->scgi_env, CONST_STR_LEN("CONTENT_LENGTH"), buf, strlen(buf)); scgi_env_add(p->scgi_env, CONST_STR_LEN("SCGI"), CONST_STR_LEN("1")); - scgi_env_add(p->scgi_env, CONST_STR_LEN("SERVER_SOFTWARE"), CONST_STR_LEN(PACKAGE_NAME"/"PACKAGE_VERSION)); - + if (buffer_is_empty(con->conf.server_tag)) { + scgi_env_add(p->scgi_env, CONST_STR_LEN("SERVER_SOFTWARE"), CONST_STR_LEN(PACKAGE_DESC)); + } else { + scgi_env_add(p->scgi_env, CONST_STR_LEN("SERVER_SOFTWARE"), CONST_BUF_LEN(con->conf.server_tag)); + } + if (con->server_name->used) { - scgi_env_add(p->scgi_env, CONST_STR_LEN("SERVER_NAME"), CONST_BUF_LEN(con->server_name)); + size_t len = con->server_name->used - 1; + + if (con->server_name->ptr[0] == '[') { + const char *colon = strstr(con->server_name->ptr, "]:"); + if (colon) len = (colon + 1) - con->server_name->ptr; + } else { + const char *colon = strchr(con->server_name->ptr, ':'); + if (colon) len = colon - con->server_name->ptr; + } + + scgi_env_add(p->scgi_env, CONST_STR_LEN("SERVER_NAME"), con->server_name->ptr, len); } else { #ifdef HAVE_IPV6 - s = inet_ntop(srv_sock->addr.plain.sa_family, - srv_sock->addr.plain.sa_family == AF_INET6 ? + s = inet_ntop(srv_sock->addr.plain.sa_family, + srv_sock->addr.plain.sa_family == AF_INET6 ? (const void *) &(srv_sock->addr.ipv6.sin6_addr) : (const void *) &(srv_sock->addr.ipv4.sin_addr), b2, sizeof(b2)-1); @@ -1451,47 +1506,47 @@ static int scgi_create_env(server *srv, handler_ctx *hctx) { #endif scgi_env_add(p->scgi_env, CONST_STR_LEN("SERVER_NAME"), s, strlen(s)); } - + scgi_env_add(p->scgi_env, CONST_STR_LEN("GATEWAY_INTERFACE"), CONST_STR_LEN("CGI/1.1")); - - ltostr(buf, + + LI_ltostr(buf, #ifdef HAVE_IPV6 ntohs(srv_sock->addr.plain.sa_family ? srv_sock->addr.ipv6.sin6_port : srv_sock->addr.ipv4.sin_port) #else ntohs(srv_sock->addr.ipv4.sin_port) #endif ); - + scgi_env_add(p->scgi_env, CONST_STR_LEN("SERVER_PORT"), buf, strlen(buf)); - + /* get the server-side of the connection to the client */ our_addr_len = sizeof(our_addr); - + if (-1 == getsockname(con->fd, &(our_addr.plain), &our_addr_len)) { s = inet_ntop_cache_get_ip(srv, &(srv_sock->addr)); } else { s = inet_ntop_cache_get_ip(srv, &(our_addr)); } scgi_env_add(p->scgi_env, CONST_STR_LEN("SERVER_ADDR"), s, strlen(s)); - - ltostr(buf, + + LI_ltostr(buf, #ifdef HAVE_IPV6 ntohs(con->dst_addr.plain.sa_family ? con->dst_addr.ipv6.sin6_port : con->dst_addr.ipv4.sin_port) #else ntohs(con->dst_addr.ipv4.sin_port) #endif ); - + scgi_env_add(p->scgi_env, CONST_STR_LEN("REMOTE_PORT"), buf, strlen(buf)); - + s = inet_ntop_cache_get_ip(srv, &(con->dst_addr)); scgi_env_add(p->scgi_env, CONST_STR_LEN("REMOTE_ADDR"), s, strlen(s)); - + if (!buffer_is_empty(con->authed_user)) { scgi_env_add(p->scgi_env, CONST_STR_LEN("REMOTE_USER"), CONST_BUF_LEN(con->authed_user)); } - + /* * SCRIPT_NAME, PATH_INFO and PATH_TRANSLATED according to @@ -1500,16 +1555,16 @@ static int scgi_create_env(server *srv, handler_ctx *hctx) { */ scgi_env_add(p->scgi_env, CONST_STR_LEN("SCRIPT_NAME"), CONST_BUF_LEN(con->uri.path)); - + if (!buffer_is_empty(con->request.pathinfo)) { scgi_env_add(p->scgi_env, CONST_STR_LEN("PATH_INFO"), CONST_BUF_LEN(con->request.pathinfo)); - + /* PATH_TRANSLATED is only defined if PATH_INFO is set */ - + if (!buffer_is_empty(host->docroot)) { buffer_copy_string_buffer(p->path, host->docroot); } else { - buffer_copy_string_buffer(p->path, con->physical.doc_root); + buffer_copy_string_buffer(p->path, con->physical.basedir); } buffer_append_string_buffer(p->path, con->request.pathinfo); scgi_env_add(p->scgi_env, CONST_STR_LEN("PATH_TRANSLATED"), CONST_BUF_LEN(p->path)); @@ -1526,21 +1581,21 @@ static int scgi_create_env(server *srv, handler_ctx *hctx) { */ if (!buffer_is_empty(host->docroot)) { - /* - * rewrite SCRIPT_FILENAME - * + /* + * rewrite SCRIPT_FILENAME + * */ - + buffer_copy_string_buffer(p->path, host->docroot); buffer_append_string_buffer(p->path, con->uri.path); - + scgi_env_add(p->scgi_env, CONST_STR_LEN("SCRIPT_FILENAME"), CONST_BUF_LEN(p->path)); scgi_env_add(p->scgi_env, CONST_STR_LEN("DOCUMENT_ROOT"), CONST_BUF_LEN(host->docroot)); } else { buffer_copy_string_buffer(p->path, con->physical.path); - + scgi_env_add(p->scgi_env, CONST_STR_LEN("SCRIPT_FILENAME"), CONST_BUF_LEN(p->path)); - scgi_env_add(p->scgi_env, CONST_STR_LEN("DOCUMENT_ROOT"), CONST_BUF_LEN(con->physical.doc_root)); + scgi_env_add(p->scgi_env, CONST_STR_LEN("DOCUMENT_ROOT"), CONST_BUF_LEN(con->physical.basedir)); } scgi_env_add(p->scgi_env, CONST_STR_LEN("REQUEST_URI"), CONST_BUF_LEN(con->request.orig_uri)); if (!buffer_is_equal(con->request.uri, con->request.orig_uri)) { @@ -1551,30 +1606,30 @@ static int scgi_create_env(server *srv, handler_ctx *hctx) { } else { scgi_env_add(p->scgi_env, CONST_STR_LEN("QUERY_STRING"), CONST_STR_LEN("")); } - + s = get_http_method_name(con->request.http_method); scgi_env_add(p->scgi_env, CONST_STR_LEN("REQUEST_METHOD"), s, strlen(s)); scgi_env_add(p->scgi_env, CONST_STR_LEN("REDIRECT_STATUS"), CONST_STR_LEN("200")); /* if php is compiled with --force-redirect */ s = get_http_version_name(con->request.http_version); scgi_env_add(p->scgi_env, CONST_STR_LEN("SERVER_PROTOCOL"), s, strlen(s)); - + #ifdef USE_OPENSSL if (srv_sock->is_ssl) { scgi_env_add(p->scgi_env, CONST_STR_LEN("HTTPS"), CONST_STR_LEN("on")); } #endif - + scgi_env_add_request_headers(srv, con, p); b = chunkqueue_get_append_buffer(hctx->wb); - + buffer_append_long(b, p->scgi_env->used); buffer_append_string_len(b, CONST_STR_LEN(":")); buffer_append_string_len(b, (const char *)p->scgi_env->ptr, p->scgi_env->used); buffer_append_string_len(b, CONST_STR_LEN(",")); hctx->wb->bytes_in += b->used - 1; - + if (con->request.content_length) { chunkqueue *req_cq = con->request_content_queue; chunk *req_c; @@ -1587,7 +1642,7 @@ static int scgi_create_env(server *srv, handler_ctx *hctx) { /* we announce toWrite octects * now take all the request_content chunk that we need to fill this request - * */ + * */ switch (req_c->type) { case FILE_CHUNK: @@ -1615,32 +1670,18 @@ static int scgi_create_env(server *srv, handler_ctx *hctx) { req_c->offset += weHave; req_cq->bytes_out += weHave; - + hctx->wb->bytes_in += weHave; break; default: break; } - + offset += weHave; } } - -#if 0 - for (i = 0; i < hctx->write_buffer->used; i++) { - fprintf(stderr, "%02x ", hctx->write_buffer->ptr[i]); - if ((i+1) % 16 == 0) { - size_t j; - for (j = i-15; j <= i; j++) { - fprintf(stderr, "%c", - isprint((unsigned char)hctx->write_buffer->ptr[j]) ? hctx->write_buffer->ptr[j] : '.'); - } - fprintf(stderr, "\n"); - } - } -#endif - + return 0; } @@ -1648,61 +1689,60 @@ static int scgi_response_parse(server *srv, connection *con, plugin_data *p, buf char *ns; const char *s; int line = 0; - + UNUSED(srv); - + buffer_copy_string_buffer(p->parse_response, in); - - for (s = p->parse_response->ptr; - NULL != (ns = (eol == EOL_RN ? strstr(s, "\r\n") : strchr(s, '\n'))); + + for (s = p->parse_response->ptr; + NULL != (ns = (eol == EOL_RN ? strstr(s, "\r\n") : strchr(s, '\n'))); s = ns + (eol == EOL_RN ? 2 : 1), line++) { const char *key, *value; int key_len; data_string *ds; - + ns[0] = '\0'; - - if (line == 0 && + + if (line == 0 && 0 == strncmp(s, "HTTP/1.", 7)) { /* non-parsed header ... we parse them anyway */ - + if ((s[7] == '1' || s[7] == '0') && s[8] == ' ') { int status; /* after the space should be a status code for us */ - + status = strtol(s+9, NULL, 10); - - if (con->http_status >= 100 && - con->http_status < 1000) { - /* we expected 3 digits and didn't got them */ + + if (status >= 100 && status < 1000) { + /* we expected 3 digits got them */ con->parsed_response |= HTTP_STATUS; con->http_status = status; } } } else { - + key = s; if (NULL == (value = strchr(s, ':'))) { /* we expect: "<key>: <value>\r\n" */ continue; } - + key_len = value - key; value += 1; - + /* skip LWS */ while (*value == ' ' || *value == '\t') value++; - + if (NULL == (ds = (data_string *)array_get_unused_element(con->response.headers, TYPE_STRING))) { ds = data_response_init(); } buffer_copy_string_len(ds->key, key, key_len); buffer_copy_string(ds->value, value); - + array_insert_unique(con->response.headers, (data_unset *)ds); - + switch(key_len) { case 4: if (0 == strncasecmp(key, "Date", key_len)) { @@ -1737,13 +1777,13 @@ static int scgi_response_parse(server *srv, connection *con, plugin_data *p, buf } } } - + /* CGI/1.1 rev 03 - 7.2.1.2 */ if ((con->parsed_response & HTTP_LOCATION) && !(con->parsed_response & HTTP_STATUS)) { con->http_status = 302; } - + return 0; } @@ -1751,10 +1791,10 @@ static int scgi_response_parse(server *srv, connection *con, plugin_data *p, buf static int scgi_demux_response(server *srv, handler_ctx *hctx) { plugin_data *p = hctx->plugin_data; connection *con = hctx->remote_conn; - + while(1) { int n; - + buffer_prepare_copy(hctx->response, 1024); if (-1 == (n = read(hctx->fd, hctx->response->ptr, hctx->response->size - 1))) { if (errno == EAGAIN || errno == EINTR) { @@ -1765,143 +1805,145 @@ static int scgi_demux_response(server *srv, handler_ctx *hctx) { log_error_write(srv, __FILE__, __LINE__, "sdd", strerror(errno), con->fd, hctx->fd); return -1; } - + if (n == 0) { /* read finished */ - + con->file_finished = 1; - + /* send final chunk */ http_chunk_append_mem(srv, con, NULL, 0); joblist_append(srv, con); - + return 1; } - + hctx->response->ptr[n] = '\0'; hctx->response->used = n+1; - + /* split header from body */ - + if (con->file_started == 0) { char *c; int in_header = 0; int header_end = 0; int cp, eol = EOL_UNSET; size_t used = 0; - + size_t hlen = 0; + buffer_append_string_buffer(hctx->response_header, hctx->response); - + /* nph (non-parsed headers) */ if (0 == strncmp(hctx->response_header->ptr, "HTTP/1.", 7)) in_header = 1; - + /* search for the \r\n\r\n or \n\n in the string */ for (c = hctx->response_header->ptr, cp = 0, used = hctx->response_header->used - 1; used; c++, cp++, used--) { if (*c == ':') in_header = 1; else if (*c == '\n') { if (in_header == 0) { /* got a response without a response header */ - + c = NULL; header_end = 1; break; } - + if (eol == EOL_UNSET) eol = EOL_N; - + if (*(c+1) == '\n') { header_end = 1; + hlen = cp + 2; break; } - + } else if (used > 1 && *c == '\r' && *(c+1) == '\n') { if (in_header == 0) { /* got a response without a response header */ - + c = NULL; header_end = 1; break; } - + if (eol == EOL_UNSET) eol = EOL_RN; - + if (used > 3 && - *(c+2) == '\r' && + *(c+2) == '\r' && *(c+3) == '\n') { header_end = 1; + hlen = cp + 4; break; } - + /* skip the \n */ c++; cp++; used--; } } - + if (header_end) { if (c == NULL) { /* no header, but a body */ - + if (con->request.http_version == HTTP_VERSION_1_1) { con->response.transfer_encoding = HTTP_TRANSFER_ENCODING_CHUNKED; } - + http_chunk_append_mem(srv, con, hctx->response_header->ptr, hctx->response_header->used); joblist_append(srv, con); } else { - size_t hlen = c - hctx->response_header->ptr + (eol == EOL_RN ? 4 : 2); size_t blen = hctx->response_header->used - hlen - 1; - + /* a small hack: terminate after at the second \r */ - hctx->response_header->used = hlen + 1 - (eol == EOL_RN ? 2 : 1); - hctx->response_header->ptr[hlen - (eol == EOL_RN ? 2 : 1)] = '\0'; - + hctx->response_header->used = hlen; + hctx->response_header->ptr[hlen - 1] = '\0'; + /* parse the response header */ scgi_response_parse(srv, con, p, hctx->response_header, eol); - + /* enable chunked-transfer-encoding */ if (con->request.http_version == HTTP_VERSION_1_1 && !(con->parsed_response & HTTP_CONTENT_LENGTH)) { con->response.transfer_encoding = HTTP_TRANSFER_ENCODING_CHUNKED; } - + if ((hctx->response->used != hlen) && blen > 0) { - http_chunk_append_mem(srv, con, c + (eol == EOL_RN ? 4: 2), blen + 1); + http_chunk_append_mem(srv, con, hctx->response_header->ptr + hlen, blen + 1); joblist_append(srv, con); } } - + con->file_started = 1; } } else { http_chunk_append_mem(srv, con, hctx->response->ptr, hctx->response->used); joblist_append(srv, con); } - -#if 0 + +#if 0 log_error_write(srv, __FILE__, __LINE__, "ddss", con->fd, hctx->fd, connection_get_state(con->state), b->ptr); #endif } - + return 0; } -int scgi_proclist_sort_up(server *srv, scgi_extension_host *host, scgi_proc *proc) { +static int scgi_proclist_sort_up(server *srv, scgi_extension_host *host, scgi_proc *proc) { scgi_proc *p; - + UNUSED(srv); - - /* we have been the smallest of the current list - * and we want to insert the node sorted as soon + + /* we have been the smallest of the current list + * and we want to insert the node sorted as soon * possible * - * 1 0 0 0 1 1 1 - * | ^ + * 1 0 0 0 1 1 1 + * | ^ * | | * +------+ - * + * */ /* nothing to sort, only one element */ @@ -1909,9 +1951,9 @@ int scgi_proclist_sort_up(server *srv, scgi_extension_host *host, scgi_proc *pro for (p = proc; p->next && p->next->load < proc->load; p = p->next); - /* no need to move something + /* no need to move something * - * 1 2 2 2 3 3 3 + * 1 2 2 2 3 3 3 * ^ * | * + @@ -1930,16 +1972,16 @@ int scgi_proclist_sort_up(server *srv, scgi_extension_host *host, scgi_proc *pro if (proc->prev) proc->prev->next = proc->next; if (proc->next) proc->next->prev = proc->prev; - + /* proc should be right of p */ - + proc->next = p->next; proc->prev = p; if (p->next) p->next->prev = proc; p->next = proc; #if 0 for(p = host->first; p; p = p->next) { - log_error_write(srv, __FILE__, __LINE__, "dd", + log_error_write(srv, __FILE__, __LINE__, "dd", p->pid, p->load); } #else @@ -1951,21 +1993,21 @@ int scgi_proclist_sort_up(server *srv, scgi_extension_host *host, scgi_proc *pro int scgi_proclist_sort_down(server *srv, scgi_extension_host *host, scgi_proc *proc) { scgi_proc *p; - + UNUSED(srv); - - /* we have been the smallest of the current list - * and we want to insert the node sorted as soon + + /* we have been the smallest of the current list + * and we want to insert the node sorted as soon * possible * - * 0 0 0 0 1 0 1 + * 0 0 0 0 1 0 1 * ^ | * | | * +----------+ * * * the basic is idea is: - * - the last active scgi process should be still + * - the last active scgi process should be still * in ram and is not swapped out yet * - processes that are not reused will be killed * after some time by the trigger-handler @@ -1975,7 +2017,7 @@ int scgi_proclist_sort_down(server *srv, scgi_extension_host *host, scgi_proc *p * ice-cold processes are propably unused since more * than 'unused-timeout', are swaped out and won't be * reused in the next seconds anyway. - * + * */ /* nothing to sort, only one element */ @@ -1984,16 +2026,16 @@ int scgi_proclist_sort_down(server *srv, scgi_extension_host *host, scgi_proc *p for (p = host->first; p != proc && p->load < proc->load; p = p->next); - /* no need to move something + /* no need to move something * - * 1 2 2 2 3 3 3 + * 1 2 2 2 3 3 3 * ^ * | * + * */ if (p == proc) return 0; - + /* we have to move left. If we are already the first element * we are done */ if (host->first == proc) return 0; @@ -2009,9 +2051,9 @@ int scgi_proclist_sort_down(server *srv, scgi_extension_host *host, scgi_proc *p p->prev = proc; if (proc->prev == NULL) host->first = proc; -#if 0 +#if 0 for(p = host->first; p; p = p->next) { - log_error_write(srv, __FILE__, __LINE__, "dd", + log_error_write(srv, __FILE__, __LINE__, "dd", p->pid, p->load); } #else @@ -2023,40 +2065,40 @@ int scgi_proclist_sort_down(server *srv, scgi_extension_host *host, scgi_proc *p static int scgi_restart_dead_procs(server *srv, plugin_data *p, scgi_extension_host *host) { scgi_proc *proc; - + for (proc = host->first; proc; proc = proc->next) { if (p->conf.debug) { - log_error_write(srv, __FILE__, __LINE__, "sbdbdddd", - "proc:", - host->host, proc->port, + log_error_write(srv, __FILE__, __LINE__, "sbdbdddd", + "proc:", + host->host, proc->port, proc->socket, proc->state, proc->is_local, proc->load, proc->pid); } - + if (0 == proc->is_local) { - /* - * external servers might get disabled - * - * enable the server again, perhaps it is back again + /* + * external servers might get disabled + * + * enable the server again, perhaps it is back again */ - + if ((proc->state == PROC_STATE_DISABLED) && (srv->cur_ts - proc->disable_ts > host->disable_time)) { proc->state = PROC_STATE_RUNNING; host->active_procs++; - - log_error_write(srv, __FILE__, __LINE__, "sbdb", - "fcgi-server re-enabled:", - host->host, host->port, + + log_error_write(srv, __FILE__, __LINE__, "sbdb", + "fcgi-server re-enabled:", + host->host, host->port, host->unixsocket); } } else { /* the child should not terminate at all */ int status; - + if (proc->state == PROC_STATE_DIED_WAIT_FOR_PID) { switch(waitpid(proc->pid, &status, WNOHANG)) { case 0: @@ -2067,33 +2109,33 @@ static int scgi_restart_dead_procs(server *srv, plugin_data *p, scgi_extension_h default: if (WIFEXITED(status)) { #if 0 - log_error_write(srv, __FILE__, __LINE__, "sdsd", + log_error_write(srv, __FILE__, __LINE__, "sdsd", "child exited, pid:", proc->pid, "status:", WEXITSTATUS(status)); #endif } else if (WIFSIGNALED(status)) { - log_error_write(srv, __FILE__, __LINE__, "sd", - "child signaled:", + log_error_write(srv, __FILE__, __LINE__, "sd", + "child signaled:", WTERMSIG(status)); } else { - log_error_write(srv, __FILE__, __LINE__, "sd", - "child died somehow:", + log_error_write(srv, __FILE__, __LINE__, "sd", + "child died somehow:", status); } - + proc->state = PROC_STATE_DIED; break; } } - - /* + + /* * local servers might died, but we restart them - * + * */ if (proc->state == PROC_STATE_DIED && proc->load == 0) { /* restart the child */ - + if (p->conf.debug) { log_error_write(srv, __FILE__, __LINE__, "ssdsbsdsd", "--- scgi spawning", @@ -2101,18 +2143,18 @@ static int scgi_restart_dead_procs(server *srv, plugin_data *p, scgi_extension_h "\n\tsocket", host->unixsocket, "\n\tcurrent:", 1, "/", host->min_procs); } - + if (scgi_spawn_connection(srv, p, host, proc)) { log_error_write(srv, __FILE__, __LINE__, "s", "ERROR: spawning fcgi failed."); return HANDLER_ERROR; } - + scgi_proclist_sort_down(srv, host, proc); } } } - + return 0; } @@ -2121,13 +2163,16 @@ static handler_t scgi_write_request(server *srv, handler_ctx *hctx) { plugin_data *p = hctx->plugin_data; scgi_extension_host *host= hctx->host; connection *con = hctx->remote_conn; - + int ret; - /* sanity check */ - if (!host || - ((!host->host->used || !host->port) && !host->unixsocket->used)) { - log_error_write(srv, __FILE__, __LINE__, "sxddd", + /* sanity check */ + if (!host) { + log_error_write(srv, __FILE__, __LINE__, "s", "fatal error: host = NULL"); + return HANDLER_ERROR; + } + if (((!host->host->used || !host->port) && !host->unixsocket->used)) { + log_error_write(srv, __FILE__, __LINE__, "sxddd", "write-req: error", host, host->host->used, @@ -2135,190 +2180,183 @@ static handler_t scgi_write_request(server *srv, handler_ctx *hctx) { host->unixsocket->used); return HANDLER_ERROR; } - + switch(hctx->state) { case FCGI_STATE_INIT: ret = host->unixsocket->used ? AF_UNIX : AF_INET; - + if (-1 == (hctx->fd = socket(ret, SOCK_STREAM, 0))) { if (errno == EMFILE || errno == EINTR) { - log_error_write(srv, __FILE__, __LINE__, "sd", + log_error_write(srv, __FILE__, __LINE__, "sd", "wait for fd at connection:", con->fd); - + return HANDLER_WAIT_FOR_FD; } - - log_error_write(srv, __FILE__, __LINE__, "ssdd", + + log_error_write(srv, __FILE__, __LINE__, "ssdd", "socket failed:", strerror(errno), srv->cur_fds, srv->max_fds); return HANDLER_ERROR; } hctx->fde_ndx = -1; - + srv->cur_fds++; - + fdevent_register(srv->ev, hctx->fd, scgi_handle_fdevent, hctx); - + if (-1 == fdevent_fcntl_set(srv->ev, hctx->fd)) { - log_error_write(srv, __FILE__, __LINE__, "ss", + log_error_write(srv, __FILE__, __LINE__, "ss", "fcntl failed: ", strerror(errno)); - + return HANDLER_ERROR; } - + /* fall through */ case FCGI_STATE_CONNECT: if (hctx->state == FCGI_STATE_INIT) { - for (hctx->proc = hctx->host->first; - hctx->proc && hctx->proc->state != PROC_STATE_RUNNING; + for (hctx->proc = hctx->host->first; + hctx->proc && hctx->proc->state != PROC_STATE_RUNNING; hctx->proc = hctx->proc->next); - + /* all childs are dead */ if (hctx->proc == NULL) { hctx->fde_ndx = -1; - + return HANDLER_ERROR; } - + if (hctx->proc->is_local) { hctx->pid = hctx->proc->pid; } - + switch (scgi_establish_connection(srv, hctx)) { case 1: scgi_set_state(srv, hctx, FCGI_STATE_CONNECT); - + /* connection is in progress, wait for an event and call getsockopt() below */ - - fdevent_event_add(srv->ev, &(hctx->fde_ndx), hctx->fd, FDEVENT_OUT); - + + fdevent_event_set(srv->ev, &(hctx->fde_ndx), hctx->fd, FDEVENT_OUT); + return HANDLER_WAIT_FOR_EVENT; case -1: /* if ECONNREFUSED choose another connection -> FIXME */ hctx->fde_ndx = -1; - + return HANDLER_ERROR; default: /* everything is ok, go on */ break; } - + } else { int socket_error; socklen_t socket_error_len = sizeof(socket_error); - + /* try to finish the connect() */ if (0 != getsockopt(hctx->fd, SOL_SOCKET, SO_ERROR, &socket_error, &socket_error_len)) { - log_error_write(srv, __FILE__, __LINE__, "ss", + log_error_write(srv, __FILE__, __LINE__, "ss", "getsockopt failed:", strerror(errno)); - + return HANDLER_ERROR; } if (socket_error != 0) { if (!hctx->proc->is_local || p->conf.debug) { /* local procs get restarted */ - + log_error_write(srv, __FILE__, __LINE__, "ss", - "establishing connection failed:", strerror(socket_error), + "establishing connection failed:", strerror(socket_error), "port:", hctx->proc->port); } - + return HANDLER_ERROR; } } - + /* ok, we have the connection */ - + hctx->proc->load++; hctx->proc->last_used = srv->cur_ts; hctx->got_proc = 1; - + if (p->conf.debug) { log_error_write(srv, __FILE__, __LINE__, "sddbdd", - "got proc:", + "got proc:", hctx->fd, - hctx->proc->pid, - hctx->proc->socket, + hctx->proc->pid, + hctx->proc->socket, hctx->proc->port, hctx->proc->load); } /* move the proc-list entry down the list */ scgi_proclist_sort_up(srv, hctx->host, hctx->proc); - + scgi_set_state(srv, hctx, FCGI_STATE_PREPARE_WRITE); /* fall through */ case FCGI_STATE_PREPARE_WRITE: scgi_create_env(srv, hctx); - + scgi_set_state(srv, hctx, FCGI_STATE_WRITE); - + /* fall through */ case FCGI_STATE_WRITE: - ret = srv->network_backend_write(srv, con, hctx->fd, hctx->wb); + ret = srv->network_backend_write(srv, con, hctx->fd, hctx->wb, MAX_WRITE_LIMIT); chunkqueue_remove_finished_chunks(hctx->wb); - - if (-1 == ret) { - if (errno == ENOTCONN) { - /* the connection got dropped after accept() - * - * this is most of the time a PHP which dies + + if (ret < 0) { + if (errno == ENOTCONN || ret == -2) { + /* the connection got dropped after accept() + * + * this is most of the time a PHP which dies * after PHP_FCGI_MAX_REQUESTS - * - */ + * + */ if (hctx->wb->bytes_out == 0 && hctx->reconnects < 5) { - usleep(10000); /* take away the load of the webserver - * to let the php a chance to restart + usleep(10000); /* take away the load of the webserver + * to let the php a chance to restart */ - + scgi_reconnect(srv, hctx); - + return HANDLER_WAIT_FOR_FD; } - + /* not reconnected ... why - * + * * far@#lighttpd report this for FreeBSD - * + * */ - - log_error_write(srv, __FILE__, __LINE__, "ssdsd", - "[REPORT ME] connection was dropped after accept(). reconnect() denied:", + + log_error_write(srv, __FILE__, __LINE__, "ssosd", + "connection was dropped after accept(). reconnect() denied:", "write-offset:", hctx->wb->bytes_out, "reconnect attempts:", hctx->reconnects); - - return HANDLER_ERROR; - } - - if ((errno != EAGAIN) && - (errno != EINTR)) { - - log_error_write(srv, __FILE__, __LINE__, "ssd", - "write failed:", strerror(errno), errno); - + return HANDLER_ERROR; } else { - fdevent_event_add(srv->ev, &(hctx->fde_ndx), hctx->fd, FDEVENT_OUT); - - return HANDLER_WAIT_FOR_EVENT; + /* -1 == ret => error on our side */ + log_error_write(srv, __FILE__, __LINE__, "ssd", + "write failed:", strerror(errno), errno); + + return HANDLER_ERROR; } } - + if (hctx->wb->bytes_out == hctx->wb->bytes_in) { /* we don't need the out event anymore */ fdevent_event_del(srv->ev, &(hctx->fde_ndx), hctx->fd); - fdevent_event_add(srv->ev, &(hctx->fde_ndx), hctx->fd, FDEVENT_IN); + fdevent_event_set(srv->ev, &(hctx->fde_ndx), hctx->fd, FDEVENT_IN); scgi_set_state(srv, hctx, FCGI_STATE_READ); } else { - fdevent_event_add(srv->ev, &(hctx->fde_ndx), hctx->fd, FDEVENT_OUT); - + fdevent_event_set(srv->ev, &(hctx->fde_ndx), hctx->fd, FDEVENT_OUT); + return HANDLER_WAIT_FOR_EVENT; } - + break; case FCGI_STATE_READ: /* waiting for a response */ @@ -2327,67 +2365,67 @@ static handler_t scgi_write_request(server *srv, handler_ctx *hctx) { log_error_write(srv, __FILE__, __LINE__, "s", "(debug) unknown state"); return HANDLER_ERROR; } - + return HANDLER_WAIT_FOR_EVENT; } SUBREQUEST_FUNC(mod_scgi_handle_subrequest) { plugin_data *p = p_d; - + handler_ctx *hctx = con->plugin_ctx[p->id]; scgi_proc *proc; scgi_extension_host *host; - + if (NULL == hctx) return HANDLER_GO_ON; - + /* not my job */ if (con->mode != p->id) return HANDLER_GO_ON; - + /* ok, create the request */ switch(scgi_write_request(srv, hctx)) { case HANDLER_ERROR: proc = hctx->proc; host = hctx->host; - - if (proc && + + if (proc && 0 == proc->is_local && proc->state != PROC_STATE_DISABLED) { /* only disable remote servers as we don't manage them*/ - - log_error_write(srv, __FILE__, __LINE__, "sbdb", "fcgi-server disabled:", + + log_error_write(srv, __FILE__, __LINE__, "sbdb", "fcgi-server disabled:", host->host, proc->port, proc->socket); - + /* disable this server */ proc->disable_ts = srv->cur_ts; proc->state = PROC_STATE_DISABLED; host->active_procs--; } - + if (hctx->state == FCGI_STATE_INIT || hctx->state == FCGI_STATE_CONNECT) { - /* connect() or getsockopt() failed, - * restart the request-handling + /* connect() or getsockopt() failed, + * restart the request-handling */ if (proc && proc->is_local) { if (p->conf.debug) { - log_error_write(srv, __FILE__, __LINE__, "sbdb", "connect() to scgi failed, restarting the request-handling:", + log_error_write(srv, __FILE__, __LINE__, "sbdb", "connect() to scgi failed, restarting the request-handling:", host->host, proc->port, proc->socket); } - /* + /* * several hctx might reference the same proc - * + * * Only one of them should mark the proc as dead all the other * ones should just take a new one. - * + * * If a new proc was started with the old struct this might lead * the mark a perfect proc as dead otherwise - * + * */ if (proc->state == PROC_STATE_RUNNING && hctx->pid == proc->pid) { @@ -2395,25 +2433,25 @@ SUBREQUEST_FUNC(mod_scgi_handle_subrequest) { } } scgi_restart_dead_procs(srv, p, host); - + scgi_connection_cleanup(srv, hctx); - + buffer_reset(con->physical.path); con->mode = DIRECT; joblist_append(srv, con); - - /* mis-using HANDLER_WAIT_FOR_FD to break out of the loop - * and hope that the childs will be restarted - * + + /* mis-using HANDLER_WAIT_FOR_FD to break out of the loop + * and hope that the childs will be restarted + * */ return HANDLER_WAIT_FOR_FD; } else { scgi_connection_cleanup(srv, hctx); - + buffer_reset(con->physical.path); con->mode = DIRECT; con->http_status = 503; - + return HANDLER_FINISHED; } case HANDLER_WAIT_FOR_EVENT: @@ -2431,35 +2469,28 @@ SUBREQUEST_FUNC(mod_scgi_handle_subrequest) { } static handler_t scgi_connection_close(server *srv, handler_ctx *hctx) { - plugin_data *p; connection *con; - + if (NULL == hctx) return HANDLER_GO_ON; - - p = hctx->plugin_data; + con = hctx->remote_conn; - - if (con->mode != p->id) return HANDLER_GO_ON; - - log_error_write(srv, __FILE__, __LINE__, "ssdsd", - "emergency exit: scgi:", + + log_error_write(srv, __FILE__, __LINE__, "ssdsd", + "emergency exit: scgi:", "connection-fd:", con->fd, "fcgi-fd:", hctx->fd); - - - + scgi_connection_cleanup(srv, hctx); - + return HANDLER_FINISHED; } -static handler_t scgi_handle_fdevent(void *s, void *ctx, int revents) { - server *srv = (server *)s; +static handler_t scgi_handle_fdevent(server *srv, void *ctx, int revents) { handler_ctx *hctx = ctx; connection *con = hctx->remote_conn; plugin_data *p = hctx->plugin_data; - + scgi_proc *proc = hctx->proc; scgi_extension_host *host= hctx->host; @@ -2471,15 +2502,15 @@ static handler_t scgi_handle_fdevent(void *s, void *ctx, int revents) { case 1: /* we are done */ scgi_connection_cleanup(srv, hctx); - + joblist_append(srv, con); return HANDLER_FINISHED; case -1: if (proc->pid && proc->state != PROC_STATE_DIED) { int status; - + /* only fetch the zombie if it is not already done */ - + switch(waitpid(proc->pid, &status, WNOHANG)) { case 0: /* child is still alive */ @@ -2489,19 +2520,19 @@ static handler_t scgi_handle_fdevent(void *s, void *ctx, int revents) { default: /* the child should not terminate at all */ if (WIFEXITED(status)) { - log_error_write(srv, __FILE__, __LINE__, "sdsd", + log_error_write(srv, __FILE__, __LINE__, "sdsd", "child exited, pid:", proc->pid, "status:", WEXITSTATUS(status)); } else if (WIFSIGNALED(status)) { - log_error_write(srv, __FILE__, __LINE__, "sd", - "child signaled:", + log_error_write(srv, __FILE__, __LINE__, "sd", + "child signaled:", WTERMSIG(status)); } else { - log_error_write(srv, __FILE__, __LINE__, "sd", - "child died somehow:", + log_error_write(srv, __FILE__, __LINE__, "sd", + "child died somehow:", status); } - + if (p->conf.debug) { log_error_write(srv, __FILE__, __LINE__, "ssdsbsdsd", "--- scgi spawning", @@ -2509,117 +2540,117 @@ static handler_t scgi_handle_fdevent(void *s, void *ctx, int revents) { "\n\tsocket", host->unixsocket, "\n\tcurrent:", 1, "/", host->min_procs); } - + if (scgi_spawn_connection(srv, p, host, proc)) { /* child died */ proc->state = PROC_STATE_DIED; } else { scgi_proclist_sort_down(srv, host, proc); } - + break; } } if (con->file_started == 0) { /* nothing has been send out yet, try to use another child */ - + if (hctx->wb->bytes_out == 0 && hctx->reconnects < 5) { scgi_reconnect(srv, hctx); - - log_error_write(srv, __FILE__, __LINE__, "sdsdsd", + + log_error_write(srv, __FILE__, __LINE__, "ssdsd", "response not sent, request not sent, reconnection.", "connection-fd:", con->fd, "fcgi-fd:", hctx->fd); - + return HANDLER_WAIT_FOR_FD; } - - log_error_write(srv, __FILE__, __LINE__, "sdsdsd", + + log_error_write(srv, __FILE__, __LINE__, "sosdsd", "response not sent, request sent:", hctx->wb->bytes_out, "connection-fd:", con->fd, "fcgi-fd:", hctx->fd); - + scgi_connection_cleanup(srv, hctx); - + connection_set_state(srv, con, CON_STATE_HANDLE_REQUEST); buffer_reset(con->physical.path); con->http_status = 500; con->mode = DIRECT; } else { /* response might have been already started, kill the connection */ - scgi_connection_cleanup(srv, hctx); - - log_error_write(srv, __FILE__, __LINE__, "ssdsd", + log_error_write(srv, __FILE__, __LINE__, "ssdsd", "response already sent out, termination connection", "connection-fd:", con->fd, "fcgi-fd:", hctx->fd); - + + scgi_connection_cleanup(srv, hctx); + connection_set_state(srv, con, CON_STATE_ERROR); } /* */ - - + + joblist_append(srv, con); return HANDLER_FINISHED; } } - + if (revents & FDEVENT_OUT) { if (hctx->state == FCGI_STATE_CONNECT || hctx->state == FCGI_STATE_WRITE) { /* we are allowed to send something out - * + * * 1. in a unfinished connect() call * 2. in a unfinished write() call (long POST request) */ return mod_scgi_handle_subrequest(srv, con, p); } else { - log_error_write(srv, __FILE__, __LINE__, "sd", - "got a FDEVENT_OUT and didn't know why:", + log_error_write(srv, __FILE__, __LINE__, "sd", + "got a FDEVENT_OUT and didn't know why:", hctx->state); } } - + /* perhaps this issue is already handled */ if (revents & FDEVENT_HUP) { if (hctx->state == FCGI_STATE_CONNECT) { /* getoptsock will catch this one (right ?) - * - * if we are in connect we might get a EINPROGRESS - * in the first call and a FDEVENT_HUP in the + * + * if we are in connect we might get a EINPROGRESS + * in the first call and a FDEVENT_HUP in the * second round - * + * * FIXME: as it is a bit ugly. - * + * */ return mod_scgi_handle_subrequest(srv, con, p); } else if (hctx->state == FCGI_STATE_READ && hctx->proc->port == 0) { /* FIXME: - * + * * ioctl says 8192 bytes to read from PHP and we receive directly a HUP for the socket * even if the FCGI_FIN packet is not received yet */ } else { - log_error_write(srv, __FILE__, __LINE__, "sbSBSDSd", - "error: unexpected close of scgi connection for", + log_error_write(srv, __FILE__, __LINE__, "sbSBSDSd", + "error: unexpected close of scgi connection for", con->uri.path, - "(no scgi process on host: ", + "(no scgi process on host: ", host->host, - ", port: ", + ", port: ", host->port, " ?)", hctx->state); - + connection_set_state(srv, con, CON_STATE_ERROR); scgi_connection_close(srv, hctx); joblist_append(srv, con); } } else if (revents & FDEVENT_ERR) { - log_error_write(srv, __FILE__, __LINE__, "s", + log_error_write(srv, __FILE__, __LINE__, "s", "fcgi: got a FDEVENT_ERR. Don't know why."); /* kill all connections to the scgi process */ @@ -2628,7 +2659,7 @@ static handler_t scgi_handle_fdevent(void *s, void *ctx, int revents) { scgi_connection_close(srv, hctx); joblist_append(srv, con); } - + return HANDLER_FINISHED; } #define PATCH(x) \ @@ -2636,22 +2667,22 @@ static handler_t scgi_handle_fdevent(void *s, void *ctx, int revents) { static int scgi_patch_connection(server *srv, connection *con, plugin_data *p) { size_t i, j; plugin_config *s = p->config_storage[0]; - + PATCH(exts); PATCH(debug); - + /* skip the first, the global context */ for (i = 1; i < srv->config_context->used; i++) { data_config *dc = (data_config *)srv->config_context->data[i]; s = p->config_storage[i]; - + /* condition didn't match */ if (!config_check_cond(srv, con, dc)) continue; - + /* merge config */ for (j = 0; j < dc->value->used; j++) { data_unset *du = dc->value->data[j]; - + if (buffer_is_equal_string(du->key, CONST_STR_LEN("scgi.server"))) { PATCH(exts); } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("scgi.debug"))) { @@ -2659,7 +2690,7 @@ static int scgi_patch_connection(server *srv, connection *con, plugin_data *p) { } } } - + return 0; } #undef PATCH @@ -2669,167 +2700,181 @@ static handler_t scgi_check_extension(server *srv, connection *con, void *p_d, i plugin_data *p = p_d; size_t s_len; int used = -1; - int ndx; size_t k; buffer *fn; scgi_extension *extension = NULL; - + scgi_extension_host *host = NULL; + + if (con->mode != DIRECT) return HANDLER_GO_ON; + /* Possibly, we processed already this request */ if (con->file_started == 1) return HANDLER_GO_ON; - + fn = uri_path_handler ? con->uri.path : con->physical.path; - if (fn->used == 0) { - return HANDLER_ERROR; - } + if (buffer_is_empty(fn)) return HANDLER_GO_ON; s_len = fn->used - 1; - + scgi_patch_connection(srv, con, p); /* check if extension matches */ for (k = 0; k < p->conf.exts->used; k++) { size_t ct_len; - - extension = p->conf.exts->exts[k]; - - if (extension->key->used == 0) continue; - - ct_len = extension->key->used - 1; - + scgi_extension *ext = p->conf.exts->exts[k]; + + if (ext->key->used == 0) continue; + + ct_len = ext->key->used - 1; + if (s_len < ct_len) continue; - + /* check extension in the form "/scgi_pattern" */ - if (*(extension->key->ptr) == '/' && strncmp(fn->ptr, extension->key->ptr, ct_len) == 0) { - break; - } else if (0 == strncmp(fn->ptr + s_len - ct_len, extension->key->ptr, ct_len)) { + if (*(ext->key->ptr) == '/') { + if (strncmp(fn->ptr, ext->key->ptr, ct_len) == 0) { + extension = ext; + break; + } + } else if (0 == strncmp(fn->ptr + s_len - ct_len, ext->key->ptr, ct_len)) { /* check extension in the form ".fcg" */ + extension = ext; break; } } /* extension doesn't match */ - if (k == p->conf.exts->used) { + if (NULL == extension) { return HANDLER_GO_ON; } - + /* get best server */ - for (k = 0, ndx = -1; k < extension->used; k++) { - scgi_extension_host *host = extension->hosts[k]; - - /* we should have at least one proc that can do somthing */ - if (host->active_procs == 0) continue; - - if (used == -1 || host->load < used) { - used = host->load; - - ndx = k; + for (k = 0; k < extension->used; k++) { + scgi_extension_host *h = extension->hosts[k]; + + /* we should have at least one proc that can do something */ + if (h->active_procs == 0) { + continue; + } + + if (used == -1 || h->load < used) { + used = h->load; + + host = h; } } - /* found a server */ - if (ndx != -1) { - scgi_extension_host *host = extension->hosts[ndx]; - - /* - * if check-local is disabled, use the uri.path handler - * - */ - - /* init handler-context */ - if (uri_path_handler) { - if (host->check_local == 0) { - handler_ctx *hctx; - char *pathinfo; - - hctx = handler_ctx_init(); - - hctx->remote_conn = con; - hctx->plugin_data = p; - hctx->host = host; - hctx->proc = NULL; - - hctx->conf.exts = p->conf.exts; - hctx->conf.debug = p->conf.debug; - - con->plugin_ctx[p->id] = hctx; - - host->load++; - - con->mode = p->id; - - if (con->conf.log_request_handling) { - log_error_write(srv, __FILE__, __LINE__, "s", "handling it in mod_scgi"); - } + if (!host) { + /* sorry, we don't have a server alive for this ext */ + buffer_reset(con->physical.path); + con->http_status = 500; - /* the prefix is the SCRIPT_NAME, - * everthing from start to the next slash - * this is important for check-local = "disable" - * - * if prefix = /admin.fcgi - * - * /admin.fcgi/foo/bar - * - * SCRIPT_NAME = /admin.fcgi - * PATH_INFO = /foo/bar - * - * if prefix = /fcgi-bin/ - * - * /fcgi-bin/foo/bar - * - * SCRIPT_NAME = /fcgi-bin/foo - * PATH_INFO = /bar - * - */ - - /* the rewrite is only done for /prefix/? matches */ - if (extension->key->ptr[0] == '/' && - con->uri.path->used > extension->key->used && - NULL != (pathinfo = strchr(con->uri.path->ptr + extension->key->used - 1, '/'))) { - /* rewrite uri.path and pathinfo */ - - buffer_copy_string(con->request.pathinfo, pathinfo); - - con->uri.path->used -= con->request.pathinfo->used - 1; - con->uri.path->ptr[con->uri.path->used - 1] = '\0'; - } - } - return HANDLER_GO_ON; - } else { + /* only send the 'no handler' once */ + if (!extension->note_is_sent) { + extension->note_is_sent = 1; + + log_error_write(srv, __FILE__, __LINE__, "sbsbs", + "all handlers for ", con->uri.path, + "on", extension->key, + "are down."); + } + + return HANDLER_FINISHED; + } + + /* a note about no handler is not sent yet */ + extension->note_is_sent = 0; + + /* + * if check-local is disabled, use the uri.path handler + * + */ + + /* init handler-context */ + if (uri_path_handler) { + if (host->check_local == 0) { handler_ctx *hctx; + char *pathinfo; + hctx = handler_ctx_init(); - + hctx->remote_conn = con; hctx->plugin_data = p; hctx->host = host; - hctx->proc = NULL; - + hctx->proc = NULL; + hctx->conf.exts = p->conf.exts; hctx->conf.debug = p->conf.debug; - + con->plugin_ctx[p->id] = hctx; - + host->load++; - + con->mode = p->id; - + if (con->conf.log_request_handling) { - log_error_write(srv, __FILE__, __LINE__, "s", "handling it in mod_fastcgi"); + log_error_write(srv, __FILE__, __LINE__, "s", + "handling it in mod_fastcgi"); } - return HANDLER_GO_ON; + /* the prefix is the SCRIPT_NAME, + * everything from start to the next slash + * this is important for check-local = "disable" + * + * if prefix = /admin.fcgi + * + * /admin.fcgi/foo/bar + * + * SCRIPT_NAME = /admin.fcgi + * PATH_INFO = /foo/bar + * + * if prefix = /fcgi-bin/ + * + * /fcgi-bin/foo/bar + * + * SCRIPT_NAME = /fcgi-bin/foo + * PATH_INFO = /bar + * + */ + + /* the rewrite is only done for /prefix/? matches */ + if (host->fix_root_path_name && extension->key->ptr[0] == '/' && extension->key->ptr[1] == '\0') { + buffer_copy_string(con->request.pathinfo, con->uri.path->ptr); + con->uri.path->used = 1; + con->uri.path->ptr[con->uri.path->used - 1] = '\0'; + } else if (extension->key->ptr[0] == '/' && + con->uri.path->used > extension->key->used && + NULL != (pathinfo = strchr(con->uri.path->ptr + extension->key->used - 1, '/'))) { + /* rewrite uri.path and pathinfo */ + + buffer_copy_string(con->request.pathinfo, pathinfo); + + con->uri.path->used -= con->request.pathinfo->used - 1; + con->uri.path->ptr[con->uri.path->used - 1] = '\0'; + } } } else { - /* no handler found */ - buffer_reset(con->physical.path); - con->http_status = 500; - - log_error_write(srv, __FILE__, __LINE__, "sb", - "no fcgi-handler found for:", - fn); - - return HANDLER_FINISHED; + handler_ctx *hctx; + hctx = handler_ctx_init(); + + hctx->remote_conn = con; + hctx->plugin_data = p; + hctx->host = host; + hctx->proc = NULL; + + hctx->conf.exts = p->conf.exts; + hctx->conf.debug = p->conf.debug; + + con->plugin_ctx[p->id] = hctx; + + host->load++; + + con->mode = p->id; + + if (con->conf.log_request_handling) { + log_error_write(srv, __FILE__, __LINE__, "s", "handling it in mod_fastcgi"); + } } + return HANDLER_GO_ON; } @@ -2846,19 +2891,19 @@ static handler_t scgi_check_extension_2(server *srv, connection *con, void *p_d) JOBLIST_FUNC(mod_scgi_handle_joblist) { plugin_data *p = p_d; handler_ctx *hctx = con->plugin_ctx[p->id]; - + if (hctx == NULL) return HANDLER_GO_ON; if (hctx->fd != -1) { switch (hctx->state) { case FCGI_STATE_READ: - fdevent_event_add(srv->ev, &(hctx->fde_ndx), hctx->fd, FDEVENT_IN); - + fdevent_event_set(srv->ev, &(hctx->fde_ndx), hctx->fd, FDEVENT_IN); + break; case FCGI_STATE_CONNECT: case FCGI_STATE_WRITE: - fdevent_event_add(srv->ev, &(hctx->fde_ndx), hctx->fd, FDEVENT_OUT); - + fdevent_event_set(srv->ev, &(hctx->fde_ndx), hctx->fd, FDEVENT_OUT); + break; case FCGI_STATE_INIT: /* at reconnect */ @@ -2875,21 +2920,21 @@ JOBLIST_FUNC(mod_scgi_handle_joblist) { static handler_t scgi_connection_close_callback(server *srv, connection *con, void *p_d) { plugin_data *p = p_d; - + return scgi_connection_close(srv, con->plugin_ctx[p->id]); } TRIGGER_FUNC(mod_scgi_handle_trigger) { plugin_data *p = p_d; size_t i, j, n; - - + + /* perhaps we should kill a connect attempt after 10-15 seconds - * + * * currently we wait for the TCP timeout which is on Linux 180 seconds - * - * - * + * + * + * */ /* check all childs if they are still up */ @@ -2906,61 +2951,61 @@ TRIGGER_FUNC(mod_scgi_handle_trigger) { scgi_extension *ex; ex = exts->exts[j]; - + for (n = 0; n < ex->used; n++) { - + scgi_proc *proc; unsigned long sum_load = 0; scgi_extension_host *host; - + host = ex->hosts[n]; - + scgi_restart_dead_procs(srv, p, host); - + for (proc = host->first; proc; proc = proc->next) { sum_load += proc->load; } - + if (host->num_procs && host->num_procs < host->max_procs && (sum_load / host->num_procs) > host->max_load_per_proc) { /* overload, spawn new child */ scgi_proc *fp = NULL; - + if (p->conf.debug) { - log_error_write(srv, __FILE__, __LINE__, "s", + log_error_write(srv, __FILE__, __LINE__, "s", "overload detected, spawning a new child"); } - + for (fp = host->unused_procs; fp && fp->pid != 0; fp = fp->next); - + if (fp) { if (fp == host->unused_procs) host->unused_procs = fp->next; - + if (fp->next) fp->next->prev = NULL; - + host->max_id++; } else { fp = scgi_process_init(); fp->id = host->max_id++; } - + host->num_procs++; - + if (buffer_is_empty(host->unixsocket)) { fp->port = host->port + fp->id; } else { buffer_copy_string_buffer(fp->socket, host->unixsocket); - buffer_append_string(fp->socket, "-"); + buffer_append_string_len(fp->socket, CONST_STR_LEN("-")); buffer_append_long(fp->socket, fp->id); } - + if (scgi_spawn_connection(srv, p, host, fp)) { log_error_write(srv, __FILE__, __LINE__, "s", "ERROR: spawning fcgi failed."); return HANDLER_ERROR; } - + fp->prev = NULL; fp->next = host->first; if (host->first) { @@ -2968,56 +3013,56 @@ TRIGGER_FUNC(mod_scgi_handle_trigger) { } host->first = fp; } - + for (proc = host->first; proc; proc = proc->next) { if (proc->load != 0) break; if (host->num_procs <= host->min_procs) break; if (proc->pid == 0) continue; - + if (srv->cur_ts - proc->last_used > host->idle_timeout) { /* a proc is idling for a long time now, * terminated it */ - + if (p->conf.debug) { - log_error_write(srv, __FILE__, __LINE__, "ssbsd", - "idle-timeout reached, terminating child:", - "socket:", proc->socket, + log_error_write(srv, __FILE__, __LINE__, "ssbsd", + "idle-timeout reached, terminating child:", + "socket:", proc->socket, "pid", proc->pid); } - - + + if (proc->next) proc->next->prev = proc->prev; if (proc->prev) proc->prev->next = proc->next; - + if (proc->prev == NULL) host->first = proc->next; - + proc->prev = NULL; proc->next = host->unused_procs; - + if (host->unused_procs) host->unused_procs->prev = proc; host->unused_procs = proc; - + kill(proc->pid, SIGTERM); - + proc->state = PROC_STATE_KILLED; - - log_error_write(srv, __FILE__, __LINE__, "ssbsd", - "killed:", - "socket:", proc->socket, + + log_error_write(srv, __FILE__, __LINE__, "ssbsd", + "killed:", + "socket:", proc->socket, "pid", proc->pid); - + host->num_procs--; - + /* proc is now in unused, let the next second handle the next process */ break; - } + } } - + for (proc = host->unused_procs; proc; proc = proc->next) { int status; - + if (proc->pid == 0) continue; - + switch (waitpid(proc->pid, &status, WNOHANG)) { case 0: /* child still running after timeout, good */ @@ -3025,10 +3070,10 @@ TRIGGER_FUNC(mod_scgi_handle_trigger) { case -1: if (errno != EINTR) { /* no PID found ? should never happen */ - log_error_write(srv, __FILE__, __LINE__, "sddss", + log_error_write(srv, __FILE__, __LINE__, "sddss", "pid ", proc->pid, proc->state, "not found:", strerror(errno)); - + #if 0 if (errno == ECHILD) { /* someone else has cleaned up for us */ @@ -3042,19 +3087,19 @@ TRIGGER_FUNC(mod_scgi_handle_trigger) { /* the child should not terminate at all */ if (WIFEXITED(status)) { if (proc->state != PROC_STATE_KILLED) { - log_error_write(srv, __FILE__, __LINE__, "sdb", - "child exited:", + log_error_write(srv, __FILE__, __LINE__, "sdb", + "child exited:", WEXITSTATUS(status), proc->socket); } } else if (WIFSIGNALED(status)) { if (WTERMSIG(status) != SIGTERM) { - log_error_write(srv, __FILE__, __LINE__, "sd", - "child signaled:", + log_error_write(srv, __FILE__, __LINE__, "sd", + "child signaled:", WTERMSIG(status)); } } else { - log_error_write(srv, __FILE__, __LINE__, "sd", - "child died somehow:", + log_error_write(srv, __FILE__, __LINE__, "sd", + "child died somehow:", status); } proc->pid = 0; @@ -3070,6 +3115,7 @@ TRIGGER_FUNC(mod_scgi_handle_trigger) { } +int mod_scgi_plugin_init(plugin *p); int mod_scgi_plugin_init(plugin *p) { p->version = LIGHTTPD_VERSION_ID; p->name = buffer_init_string("scgi"); @@ -3084,8 +3130,8 @@ int mod_scgi_plugin_init(plugin *p) { p->handle_subrequest = mod_scgi_handle_subrequest; p->handle_joblist = mod_scgi_handle_joblist; p->handle_trigger = mod_scgi_handle_trigger; - + p->data = NULL; - + return 0; } diff --git a/src/mod_secure_download.c b/src/mod_secure_download.c index 1ea5a50..a9c031f 100644 --- a/src/mod_secure_download.c +++ b/src/mod_secure_download.c @@ -1,22 +1,14 @@ -#include <ctype.h> -#include <stdlib.h> -#include <string.h> - #include "base.h" #include "log.h" #include "buffer.h" #include "plugin.h" -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif +#include <ctype.h> +#include <stdlib.h> +#include <string.h> -#ifdef USE_OPENSSL -# include <openssl/md5.h> -#else -# include "md5.h" -#endif +#include "md5.h" #define HASHLEN 16 typedef unsigned char HASH[HASHLEN]; @@ -25,7 +17,7 @@ typedef char HASHHEX[HASHHEXLEN+1]; #ifdef USE_OPENSSL #define IN const #else -#define IN +#define IN #endif #define OUT @@ -36,28 +28,28 @@ typedef struct { buffer *doc_root; buffer *secret; buffer *uri_prefix; - - unsigned short timeout; + + unsigned int timeout; } plugin_config; typedef struct { PLUGIN_DATA; - + buffer *md5; - + plugin_config **config_storage; - - plugin_config conf; + + plugin_config conf; } plugin_data; /* init the plugin data */ INIT_FUNC(mod_secdownload_init) { plugin_data *p; - + p = calloc(1, sizeof(*p)); - + p->md5 = buffer_init(); - + return p; } @@ -65,27 +57,27 @@ INIT_FUNC(mod_secdownload_init) { FREE_FUNC(mod_secdownload_free) { plugin_data *p = p_d; UNUSED(srv); - + if (!p) return HANDLER_GO_ON; - + if (p->config_storage) { size_t i; for (i = 0; i < srv->config_context->used; i++) { plugin_config *s = p->config_storage[i]; - + buffer_free(s->secret); buffer_free(s->doc_root); buffer_free(s->uri_prefix); - + free(s); } free(p->config_storage); } - + buffer_free(p->md5); - + free(p); - + return HANDLER_GO_ON; } @@ -94,65 +86,65 @@ FREE_FUNC(mod_secdownload_free) { SETDEFAULTS_FUNC(mod_secdownload_set_defaults) { plugin_data *p = p_d; size_t i = 0; - - config_values_t cv[] = { + + config_values_t cv[] = { { "secdownload.secret", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 0 */ { "secdownload.document-root", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 1 */ { "secdownload.uri-prefix", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 2 */ - { "secdownload.timeout", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 3 */ + { "secdownload.timeout", NULL, T_CONFIG_INT, T_CONFIG_SCOPE_CONNECTION }, /* 3 */ { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET } }; - + if (!p) return HANDLER_ERROR; - + p->config_storage = calloc(1, srv->config_context->used * sizeof(specific_config *)); - + for (i = 0; i < srv->config_context->used; i++) { plugin_config *s; - + s = calloc(1, sizeof(plugin_config)); s->secret = buffer_init(); s->doc_root = buffer_init(); s->uri_prefix = buffer_init(); s->timeout = 60; - + cv[0].destination = s->secret; cv[1].destination = s->doc_root; cv[2].destination = s->uri_prefix; cv[3].destination = &(s->timeout); - + p->config_storage[i] = s; - + if (0 != config_insert_values_global(srv, ((data_config *)srv->config_context->data[i])->value, cv)) { return HANDLER_ERROR; } } - + return HANDLER_GO_ON; } /** * checks if the supplied string is a MD5 string - * + * * @param str a possible MD5 string * @return if the supplied string is a valid MD5 string 1 is returned otherwise 0 */ -int is_hex_len(const char *str, size_t len) { +static int is_hex_len(const char *str, size_t len) { size_t i; - + if (NULL == str) return 0; - + for (i = 0; i < len && *str; i++, str++) { /* illegal characters */ if (!((*str >= '0' && *str <= '9') || (*str >= 'a' && *str <= 'f') || - (*str >= 'A' && *str <= 'F')) + (*str >= 'A' && *str <= 'F')) ) { return 0; } } - + return i == len; } @@ -161,24 +153,24 @@ int is_hex_len(const char *str, size_t len) { static int mod_secdownload_patch_connection(server *srv, connection *con, plugin_data *p) { size_t i, j; plugin_config *s = p->config_storage[0]; - + PATCH(secret); PATCH(doc_root); PATCH(uri_prefix); PATCH(timeout); - + /* skip the first, the global context */ for (i = 1; i < srv->config_context->used; i++) { data_config *dc = (data_config *)srv->config_context->data[i]; s = p->config_storage[i]; - + /* condition didn't match */ if (!config_check_cond(srv, con, dc)) continue; - + /* merge config */ for (j = 0; j < dc->value->used; j++) { data_unset *du = dc->value->data[j]; - + if (buffer_is_equal_string(du->key, CONST_STR_LEN("secdownload.secret"))) { PATCH(secret); } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("secdownload.document-root"))) { @@ -190,7 +182,7 @@ static int mod_secdownload_patch_connection(server *srv, connection *con, plugin } } } - + return 0; } #undef PATCH @@ -198,108 +190,112 @@ static int mod_secdownload_patch_connection(server *srv, connection *con, plugin URIHANDLER_FUNC(mod_secdownload_uri_handler) { plugin_data *p = p_d; - MD5_CTX Md5Ctx; + li_MD5_CTX Md5Ctx; HASH HA1; const char *rel_uri, *ts_str, *md5_str; time_t ts = 0; size_t i; - + + if (con->mode != DIRECT) return HANDLER_GO_ON; + if (con->uri.path->used == 0) return HANDLER_GO_ON; - + mod_secdownload_patch_connection(srv, con, p); if (buffer_is_empty(p->conf.uri_prefix)) return HANDLER_GO_ON; - + if (buffer_is_empty(p->conf.secret)) { log_error_write(srv, __FILE__, __LINE__, "s", "secdownload.secret has to be set"); return HANDLER_ERROR; } - + if (buffer_is_empty(p->conf.doc_root)) { log_error_write(srv, __FILE__, __LINE__, "s", "secdownload.document-root has to be set"); return HANDLER_ERROR; } - - /* + + /* * /<uri-prefix>[a-f0-9]{32}/[a-f0-9]{8}/<rel-path> */ - + if (0 != strncmp(con->uri.path->ptr, p->conf.uri_prefix->ptr, p->conf.uri_prefix->used - 1)) return HANDLER_GO_ON; - + md5_str = con->uri.path->ptr + p->conf.uri_prefix->used - 1; - + if (!is_hex_len(md5_str, 32)) return HANDLER_GO_ON; if (*(md5_str + 32) != '/') return HANDLER_GO_ON; - + ts_str = md5_str + 32 + 1; - + if (!is_hex_len(ts_str, 8)) return HANDLER_GO_ON; if (*(ts_str + 8) != '/') return HANDLER_GO_ON; - + for (i = 0; i < 8; i++) { ts = (ts << 4) + hex2int(*(ts_str + i)); } - + /* timed-out */ - if (srv->cur_ts - ts > p->conf.timeout || - srv->cur_ts - ts < -p->conf.timeout) { - con->http_status = 408; - + if ( (srv->cur_ts > ts && (unsigned int) (srv->cur_ts - ts) > p->conf.timeout) || + (srv->cur_ts < ts && (unsigned int) (ts - srv->cur_ts) > p->conf.timeout) ) { + /* "Gone" as the url will never be valid again instead of "408 - Timeout" where the request may be repeated */ + con->http_status = 410; + return HANDLER_FINISHED; } - + rel_uri = ts_str + 8; - - /* checking MD5 - * + + /* checking MD5 + * * <secret><rel-path><timestamp-hex> */ - + buffer_copy_string_buffer(p->md5, p->conf.secret); buffer_append_string(p->md5, rel_uri); buffer_append_string_len(p->md5, ts_str, 8); - - MD5_Init(&Md5Ctx); - MD5_Update(&Md5Ctx, (unsigned char *)p->md5->ptr, p->md5->used - 1); - MD5_Final(HA1, &Md5Ctx); - + + li_MD5_Init(&Md5Ctx); + li_MD5_Update(&Md5Ctx, (unsigned char *)p->md5->ptr, p->md5->used - 1); + li_MD5_Final(HA1, &Md5Ctx); + buffer_copy_string_hex(p->md5, (char *)HA1, 16); - - if (0 != strncmp(md5_str, p->md5->ptr, 32)) { + + if (0 != strncasecmp(md5_str, p->md5->ptr, 32)) { con->http_status = 403; - - log_error_write(srv, __FILE__, __LINE__, "sss", + + log_error_write(srv, __FILE__, __LINE__, "sss", "md5 invalid:", md5_str, p->md5->ptr); - + return HANDLER_FINISHED; } - + /* starting with the last / we should have relative-path to the docroot */ - + buffer_copy_string_buffer(con->physical.doc_root, p->conf.doc_root); buffer_copy_string(con->physical.rel_path, rel_uri); buffer_copy_string_buffer(con->physical.path, con->physical.doc_root); buffer_append_string_buffer(con->physical.path, con->physical.rel_path); - + return HANDLER_GO_ON; } /* this function is called at dlopen() time and inits the callbacks */ +int mod_secdownload_plugin_init(plugin *p); int mod_secdownload_plugin_init(plugin *p) { p->version = LIGHTTPD_VERSION_ID; p->name = buffer_init_string("secdownload"); - + p->init = mod_secdownload_init; p->handle_physical = mod_secdownload_uri_handler; p->set_defaults = mod_secdownload_set_defaults; p->cleanup = mod_secdownload_free; - + p->data = NULL; - + return 0; } diff --git a/src/mod_setenv.c b/src/mod_setenv.c index 9501554..d9cf94d 100644 --- a/src/mod_setenv.c +++ b/src/mod_setenv.c @@ -1,6 +1,3 @@ -#include <stdlib.h> -#include <string.h> - #include "base.h" #include "log.h" #include "buffer.h" @@ -9,6 +6,9 @@ #include "response.h" +#include <stdlib.h> +#include <string.h> + /* plugin config for all request/connections */ typedef struct { @@ -18,25 +18,25 @@ typedef struct { typedef struct { array *request_header; array *response_header; - + array *environment; } plugin_config; typedef struct { PLUGIN_DATA; - + plugin_config **config_storage; - - plugin_config conf; + + plugin_config conf; } plugin_data; -static handler_ctx * handler_ctx_init() { +static handler_ctx * handler_ctx_init(void) { handler_ctx * hctx; - + hctx = calloc(1, sizeof(*hctx)); - + hctx->handled = 0; - + return hctx; } @@ -48,36 +48,36 @@ static void handler_ctx_free(handler_ctx *hctx) { /* init the plugin data */ INIT_FUNC(mod_setenv_init) { plugin_data *p; - + p = calloc(1, sizeof(*p)); - + return p; } /* detroy the plugin data */ FREE_FUNC(mod_setenv_free) { plugin_data *p = p_d; - + UNUSED(srv); if (!p) return HANDLER_GO_ON; - + if (p->config_storage) { size_t i; for (i = 0; i < srv->config_context->used; i++) { plugin_config *s = p->config_storage[i]; - + array_free(s->request_header); array_free(s->response_header); array_free(s->environment); - + free(s); } free(p->config_storage); } - + free(p); - + return HANDLER_GO_ON; } @@ -86,37 +86,37 @@ FREE_FUNC(mod_setenv_free) { SETDEFAULTS_FUNC(mod_setenv_set_defaults) { plugin_data *p = p_d; size_t i = 0; - - config_values_t cv[] = { + + config_values_t cv[] = { { "setenv.add-request-header", NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION }, /* 0 */ { "setenv.add-response-header", NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION }, /* 1 */ { "setenv.add-environment", NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION }, /* 2 */ { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET } }; - + if (!p) return HANDLER_ERROR; - + p->config_storage = calloc(1, srv->config_context->used * sizeof(specific_config *)); - + for (i = 0; i < srv->config_context->used; i++) { plugin_config *s; - + s = calloc(1, sizeof(plugin_config)); s->request_header = array_init(); s->response_header = array_init(); s->environment = array_init(); - + cv[0].destination = s->request_header; cv[1].destination = s->response_header; cv[2].destination = s->environment; - + p->config_storage[i] = s; - + if (0 != config_insert_values_global(srv, ((data_config *)srv->config_context->data[i])->value, cv)) { return HANDLER_ERROR; } } - + return HANDLER_GO_ON; } @@ -125,23 +125,23 @@ SETDEFAULTS_FUNC(mod_setenv_set_defaults) { static int mod_setenv_patch_connection(server *srv, connection *con, plugin_data *p) { size_t i, j; plugin_config *s = p->config_storage[0]; - + PATCH(request_header); PATCH(response_header); PATCH(environment); - + /* skip the first, the global context */ for (i = 1; i < srv->config_context->used; i++) { data_config *dc = (data_config *)srv->config_context->data[i]; s = p->config_storage[i]; - + /* condition didn't match */ if (!config_check_cond(srv, con, dc)) continue; - + /* merge config */ for (j = 0; j < dc->value->used; j++) { data_unset *du = dc->value->data[j]; - + if (buffer_is_equal_string(du->key, CONST_STR_LEN("setenv.add-request-header"))) { PATCH(request_header); } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("setenv.add-response-header"))) { @@ -151,7 +151,7 @@ static int mod_setenv_patch_connection(server *srv, connection *con, plugin_data } } } - + return 0; } #undef PATCH @@ -160,12 +160,12 @@ URIHANDLER_FUNC(mod_setenv_uri_handler) { plugin_data *p = p_d; size_t k; handler_ctx *hctx; - + if (con->plugin_ctx[p->id]) { hctx = con->plugin_ctx[p->id]; } else { hctx = handler_ctx_init(); - + con->plugin_ctx[p->id] = hctx; } @@ -180,68 +180,69 @@ URIHANDLER_FUNC(mod_setenv_uri_handler) { for (k = 0; k < p->conf.request_header->used; k++) { data_string *ds = (data_string *)p->conf.request_header->data[k]; data_string *ds_dst; - + if (NULL == (ds_dst = (data_string *)array_get_unused_element(con->request.headers, TYPE_STRING))) { ds_dst = data_string_init(); } - + buffer_copy_string_buffer(ds_dst->key, ds->key); buffer_copy_string_buffer(ds_dst->value, ds->value); - + array_insert_unique(con->request.headers, (data_unset *)ds_dst); } - + for (k = 0; k < p->conf.environment->used; k++) { data_string *ds = (data_string *)p->conf.environment->data[k]; data_string *ds_dst; - + if (NULL == (ds_dst = (data_string *)array_get_unused_element(con->environment, TYPE_STRING))) { ds_dst = data_string_init(); } - + buffer_copy_string_buffer(ds_dst->key, ds->key); buffer_copy_string_buffer(ds_dst->value, ds->value); - + array_insert_unique(con->environment, (data_unset *)ds_dst); } - + for (k = 0; k < p->conf.response_header->used; k++) { data_string *ds = (data_string *)p->conf.response_header->data[k]; - + response_header_insert(srv, con, CONST_BUF_LEN(ds->key), CONST_BUF_LEN(ds->value)); } - + /* not found */ return HANDLER_GO_ON; } -REQUESTDONE_FUNC(mod_setenv_reset) { +CONNECTION_FUNC(mod_setenv_reset) { plugin_data *p = p_d; - + UNUSED(srv); - + if (con->plugin_ctx[p->id]) { handler_ctx_free(con->plugin_ctx[p->id]); con->plugin_ctx[p->id] = NULL; } - return HANDLER_GO_ON; + return HANDLER_GO_ON; } /* this function is called at dlopen() time and inits the callbacks */ +int mod_setenv_plugin_init(plugin *p); int mod_setenv_plugin_init(plugin *p) { p->version = LIGHTTPD_VERSION_ID; p->name = buffer_init_string("setenv"); - + p->init = mod_setenv_init; p->handle_uri_clean = mod_setenv_uri_handler; p->set_defaults = mod_setenv_set_defaults; p->cleanup = mod_setenv_free; - - p->handle_request_done = mod_setenv_reset; + + p->connection_reset = mod_setenv_reset; p->data = NULL; - + return 0; } diff --git a/src/mod_simple_vhost.c b/src/mod_simple_vhost.c index 8f81384..bbb61d5 100644 --- a/src/mod_simple_vhost.c +++ b/src/mod_simple_vhost.c @@ -1,8 +1,3 @@ -#include <ctype.h> -#include <stdlib.h> -#include <string.h> -#include <errno.h> - #include "base.h" #include "log.h" #include "buffer.h" @@ -10,15 +5,16 @@ #include "plugin.h" -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif +#include <ctype.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> typedef struct { buffer *server_root; buffer *default_host; buffer *document_root; - + buffer *docroot_cache_key; buffer *docroot_cache_value; buffer *docroot_cache_servername; @@ -28,119 +24,119 @@ typedef struct { typedef struct { PLUGIN_DATA; - + buffer *doc_root; - + plugin_config **config_storage; - plugin_config conf; + plugin_config conf; } plugin_data; INIT_FUNC(mod_simple_vhost_init) { plugin_data *p; - + p = calloc(1, sizeof(*p)); - + p->doc_root = buffer_init(); - + return p; } FREE_FUNC(mod_simple_vhost_free) { plugin_data *p = p_d; - + UNUSED(srv); if (!p) return HANDLER_GO_ON; - + if (p->config_storage) { size_t i; for (i = 0; i < srv->config_context->used; i++) { plugin_config *s = p->config_storage[i]; - + buffer_free(s->document_root); buffer_free(s->default_host); buffer_free(s->server_root); - + buffer_free(s->docroot_cache_key); buffer_free(s->docroot_cache_value); buffer_free(s->docroot_cache_servername); - + free(s); } - + free(p->config_storage); } - + buffer_free(p->doc_root); - + free(p); - + return HANDLER_GO_ON; } SETDEFAULTS_FUNC(mod_simple_vhost_set_defaults) { plugin_data *p = p_d; size_t i; - - config_values_t cv[] = { + + config_values_t cv[] = { { "simple-vhost.server-root", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, { "simple-vhost.default-host", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, { "simple-vhost.document-root", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, { "simple-vhost.debug", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET } }; - + if (!p) return HANDLER_ERROR; - + p->config_storage = calloc(1, srv->config_context->used * sizeof(specific_config *)); - + for (i = 0; i < srv->config_context->used; i++) { plugin_config *s; - + s = calloc(1, sizeof(plugin_config)); - + s->server_root = buffer_init(); s->default_host = buffer_init(); s->document_root = buffer_init(); - + s->docroot_cache_key = buffer_init(); s->docroot_cache_value = buffer_init(); s->docroot_cache_servername = buffer_init(); s->debug = 0; - + cv[0].destination = s->server_root; cv[1].destination = s->default_host; cv[2].destination = s->document_root; cv[3].destination = &(s->debug); - - + + p->config_storage[i] = s; - + if (0 != config_insert_values_global(srv, ((data_config *)srv->config_context->data[i])->value, cv)) { return HANDLER_ERROR; } } - + return HANDLER_GO_ON; } static int build_doc_root(server *srv, connection *con, plugin_data *p, buffer *out, buffer *host) { stat_cache_entry *sce = NULL; - + buffer_prepare_copy(out, 128); if (p->conf.server_root->used) { buffer_copy_string_buffer(out, p->conf.server_root); - + if (host->used) { /* a hostname has to start with a alpha-numerical character * and must not contain a slash "/" */ char *dp; - + BUFFER_APPEND_SLASH(out); - + if (NULL == (dp = strchr(host->ptr, ':'))) { buffer_append_string_buffer(out, host); } else { @@ -148,7 +144,7 @@ static int build_doc_root(server *srv, connection *con, plugin_data *p, buffer * } } BUFFER_APPEND_SLASH(out); - + if (p->conf.document_root->used > 2 && p->conf.document_root->ptr[0] == '/') { buffer_append_string_len(out, p->conf.document_root->ptr + 1, p->conf.document_root->used - 2); } else { @@ -159,7 +155,7 @@ static int build_doc_root(server *srv, connection *con, plugin_data *p, buffer * buffer_copy_string_buffer(out, con->conf.document_root); BUFFER_APPEND_SLASH(out); } - + if (HANDLER_ERROR == stat_cache_get_entry(srv, con, out, &sce)) { if (p->conf.debug) { log_error_write(srv, __FILE__, __LINE__, "sb", @@ -169,7 +165,7 @@ static int build_doc_root(server *srv, connection *con, plugin_data *p, buffer * } else if (!S_ISDIR(sce->st.st_mode)) { return -1; } - + return 0; } @@ -179,29 +175,29 @@ static int build_doc_root(server *srv, connection *con, plugin_data *p, buffer * static int mod_simple_vhost_patch_connection(server *srv, connection *con, plugin_data *p) { size_t i, j; plugin_config *s = p->config_storage[0]; - + PATCH(server_root); PATCH(default_host); PATCH(document_root); - + PATCH(docroot_cache_key); PATCH(docroot_cache_value); PATCH(docroot_cache_servername); PATCH(debug); - + /* skip the first, the global context */ for (i = 1; i < srv->config_context->used; i++) { data_config *dc = (data_config *)srv->config_context->data[i]; s = p->config_storage[i]; - + /* condition didn't match */ if (!config_check_cond(srv, con, dc)) continue; - + /* merge config */ for (j = 0; j < dc->value->used; j++) { data_unset *du = dc->value->data[j]; - + if (buffer_is_equal_string(du->key, CONST_STR_LEN("simple-vhost.server-root"))) { PATCH(server_root); PATCH(docroot_cache_key); @@ -216,7 +212,7 @@ static int mod_simple_vhost_patch_connection(server *srv, connection *con, plugi } } } - + return 0; } #undef PATCH @@ -227,12 +223,12 @@ static handler_t mod_simple_vhost_docroot(server *srv, connection *con, void *p_ /* * cache the last successfull translation from hostname (authority) to docroot * - this saves us a stat() call - * + * */ - + mod_simple_vhost_patch_connection(srv, con, p); - - if (p->conf.docroot_cache_key->used && + + if (p->conf.docroot_cache_key->used && con->uri.authority->used && buffer_is_equal(p->conf.docroot_cache_key, con->uri.authority)) { /* cache hit */ @@ -243,39 +239,44 @@ static handler_t mod_simple_vhost_docroot(server *srv, connection *con, void *p_ if ((con->uri.authority->used == 0) || build_doc_root(srv, con, p, p->doc_root, con->uri.authority)) { /* not found, fallback the default-host */ - if (build_doc_root(srv, con, p, - p->doc_root, + if (build_doc_root(srv, con, p, + p->doc_root, p->conf.default_host)) { return HANDLER_GO_ON; } else { buffer_copy_string_buffer(con->server_name, p->conf.default_host); + buffer_copy_string_buffer(con->physical.doc_root, p->doc_root); + + /* do not cache default host */ + return HANDLER_GO_ON; } } else { buffer_copy_string_buffer(con->server_name, con->uri.authority); } - + /* copy to cache */ buffer_copy_string_buffer(p->conf.docroot_cache_key, con->uri.authority); buffer_copy_string_buffer(p->conf.docroot_cache_value, p->doc_root); buffer_copy_string_buffer(p->conf.docroot_cache_servername, con->server_name); - + buffer_copy_string_buffer(con->physical.doc_root, p->doc_root); } - + return HANDLER_GO_ON; } +int mod_simple_vhost_plugin_init(plugin *p); int mod_simple_vhost_plugin_init(plugin *p) { p->version = LIGHTTPD_VERSION_ID; p->name = buffer_init_string("simple_vhost"); - + p->init = mod_simple_vhost_init; p->set_defaults = mod_simple_vhost_set_defaults; p->handle_docroot = mod_simple_vhost_docroot; p->cleanup = mod_simple_vhost_free; - + p->data = NULL; - + return 0; } diff --git a/src/mod_skeleton.c b/src/mod_skeleton.c index a3fa186..66de2b1 100644 --- a/src/mod_skeleton.c +++ b/src/mod_skeleton.c @@ -1,26 +1,22 @@ -#include <ctype.h> -#include <stdlib.h> -#include <string.h> - #include "base.h" #include "log.h" #include "buffer.h" #include "plugin.h" -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif +#include <ctype.h> +#include <stdlib.h> +#include <string.h> /** * this is a skeleton for a lighttpd plugin - * + * * just replaces every occurance of 'skeleton' by your plugin name - * + * * e.g. in vim: - * + * * :%s/skeleton/myhandler/ - * + * */ @@ -33,12 +29,12 @@ typedef struct { typedef struct { PLUGIN_DATA; - + buffer *match_buf; - + plugin_config **config_storage; - - plugin_config conf; + + plugin_config conf; } plugin_data; typedef struct { @@ -47,36 +43,36 @@ typedef struct { static handler_ctx * handler_ctx_init() { handler_ctx * hctx; - + hctx = calloc(1, sizeof(*hctx)); - + return hctx; } static void handler_ctx_free(handler_ctx *hctx) { - + free(hctx); } /* init the plugin data */ INIT_FUNC(mod_skeleton_init) { plugin_data *p; - + p = calloc(1, sizeof(*p)); - + p->match_buf = buffer_init(); - + return p; } /* detroy the plugin data */ FREE_FUNC(mod_skeleton_free) { plugin_data *p = p_d; - + UNUSED(srv); if (!p) return HANDLER_GO_ON; - + if (p->config_storage) { size_t i; @@ -84,18 +80,18 @@ FREE_FUNC(mod_skeleton_free) { plugin_config *s = p->config_storage[i]; if (!s) continue; - + array_free(s->match); - + free(s); } free(p->config_storage); } - + buffer_free(p->match_buf); - + free(p); - + return HANDLER_GO_ON; } @@ -104,31 +100,31 @@ FREE_FUNC(mod_skeleton_free) { SETDEFAULTS_FUNC(mod_skeleton_set_defaults) { plugin_data *p = p_d; size_t i = 0; - - config_values_t cv[] = { + + config_values_t cv[] = { { "skeleton.array", NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION }, /* 0 */ { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET } }; - + if (!p) return HANDLER_ERROR; - + p->config_storage = calloc(1, srv->config_context->used * sizeof(specific_config *)); - + for (i = 0; i < srv->config_context->used; i++) { plugin_config *s; - + s = calloc(1, sizeof(plugin_config)); s->match = array_init(); - + cv[0].destination = s->match; - + p->config_storage[i] = s; - + if (0 != config_insert_values_global(srv, ((data_config *)srv->config_context->data[i])->value, cv)) { return HANDLER_ERROR; } } - + return HANDLER_GO_ON; } @@ -137,27 +133,27 @@ SETDEFAULTS_FUNC(mod_skeleton_set_defaults) { static int mod_skeleton_patch_connection(server *srv, connection *con, plugin_data *p) { size_t i, j; plugin_config *s = p->config_storage[0]; - + PATCH(match); - + /* skip the first, the global context */ for (i = 1; i < srv->config_context->used; i++) { data_config *dc = (data_config *)srv->config_context->data[i]; s = p->config_storage[i]; - + /* condition didn't match */ if (!config_check_cond(srv, con, dc)) continue; - + /* merge config */ for (j = 0; j < dc->value->used; j++) { data_unset *du = dc->value->data[j]; - + if (buffer_is_equal_string(du->key, CONST_STR_LEN("skeleton.array"))) { PATCH(match); } } } - + return 0; } #undef PATCH @@ -166,29 +162,31 @@ URIHANDLER_FUNC(mod_skeleton_uri_handler) { plugin_data *p = p_d; int s_len; size_t k, i; - + UNUSED(srv); + if (con->mode != DIRECT) return HANDLER_GO_ON; + if (con->uri.path->used == 0) return HANDLER_GO_ON; - + mod_skeleton_patch_connection(srv, con, p); s_len = con->uri.path->used - 1; - + for (k = 0; k < p->conf.match->used; k++) { data_string *ds = (data_string *)p->conf.match->data[k]; int ct_len = ds->value->used - 1; - + if (ct_len > s_len) continue; if (ds->value->used == 0) continue; - + if (0 == strncmp(con->uri.path->ptr + s_len - ct_len, ds->value->ptr, ct_len)) { con->http_status = 403; - + return HANDLER_FINISHED; } } - + /* not found */ return HANDLER_GO_ON; } @@ -198,13 +196,13 @@ URIHANDLER_FUNC(mod_skeleton_uri_handler) { int mod_skeleton_plugin_init(plugin *p) { p->version = LIGHTTPD_VERSION_ID; p->name = buffer_init_string("skeleton"); - + p->init = mod_skeleton_init; p->handle_uri_clean = mod_skeleton_uri_handler; p->set_defaults = mod_skeleton_set_defaults; p->cleanup = mod_skeleton_free; - + p->data = NULL; - + return 0; } diff --git a/src/mod_ssi.c b/src/mod_ssi.c index b6a19d7..897f49e 100644 --- a/src/mod_ssi.c +++ b/src/mod_ssi.c @@ -1,13 +1,3 @@ -#include <sys/types.h> - -#include <ctype.h> -#include <stdlib.h> -#include <stdio.h> -#include <string.h> -#include <errno.h> -#include <time.h> -#include <unistd.h> - #include "base.h" #include "log.h" #include "buffer.h" @@ -24,30 +14,46 @@ #include "sys-socket.h" +#include <sys/types.h> + +#include <ctype.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include <time.h> +#include <unistd.h> + #ifdef HAVE_PWD_H -#include <pwd.h> +# include <pwd.h> #endif #ifdef HAVE_FORK -#include <sys/wait.h> +# include <sys/wait.h> #endif #ifdef HAVE_SYS_FILIO_H -#include <sys/filio.h> +# include <sys/filio.h> #endif +#include "etag.h" +#include "version.h" + +/* The newest modified time of included files for include statement */ +static volatile time_t include_file_last_mtime = 0; + /* init the plugin data */ INIT_FUNC(mod_ssi_init) { plugin_data *p; - + p = calloc(1, sizeof(*p)); - + p->timefmt = buffer_init(); p->stat_fn = buffer_init(); - + p->ssi_vars = array_init(); p->ssi_cgi_env = array_init(); - + return p; } @@ -55,21 +61,22 @@ INIT_FUNC(mod_ssi_init) { FREE_FUNC(mod_ssi_free) { plugin_data *p = p_d; UNUSED(srv); - + if (!p) return HANDLER_GO_ON; - + if (p->config_storage) { size_t i; for (i = 0; i < srv->config_context->used; i++) { plugin_config *s = p->config_storage[i]; - + array_free(s->ssi_extension); - + buffer_free(s->content_type); + free(s); } free(p->config_storage); } - + array_free(p->ssi_vars); array_free(p->ssi_cgi_env); #ifdef HAVE_PCRE_H @@ -77,9 +84,9 @@ FREE_FUNC(mod_ssi_free) { #endif buffer_free(p->timefmt); buffer_free(p->stat_fn); - + free(p); - + return HANDLER_GO_ON; } @@ -92,36 +99,39 @@ SETDEFAULTS_FUNC(mod_ssi_set_defaults) { const char *errptr; int erroff; #endif - - config_values_t cv[] = { + + config_values_t cv[] = { { "ssi.extension", NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION }, /* 0 */ + { "ssi.content-type", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 1 */ { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET } }; - + if (!p) return HANDLER_ERROR; - + p->config_storage = calloc(1, srv->config_context->used * sizeof(specific_config *)); - + for (i = 0; i < srv->config_context->used; i++) { plugin_config *s; - + s = calloc(1, sizeof(plugin_config)); s->ssi_extension = array_init(); - + s->content_type = buffer_init(); + cv[0].destination = s->ssi_extension; - + cv[1].destination = s->content_type; + p->config_storage[i] = s; - + if (0 != config_insert_values_global(srv, ((data_config *)srv->config_context->data[i])->value, cv)) { return HANDLER_ERROR; } } - + #ifdef HAVE_PCRE_H /* allow 2 params */ if (NULL == (p->ssi_regex = pcre_compile("<!--#([a-z]+)\\s+(?:([a-z]+)=\"(.*?)(?<!\\\\)\"\\s*)?(?:([a-z]+)=\"(.*?)(?<!\\\\)\"\\s*)?-->", 0, &errptr, &erroff, NULL))) { log_error_write(srv, __FILE__, __LINE__, "sds", - "ssi: pcre ", + "ssi: pcre ", erroff, errptr); return HANDLER_ERROR; } @@ -130,52 +140,52 @@ SETDEFAULTS_FUNC(mod_ssi_set_defaults) { "mod_ssi: pcre support is missing, please recompile with pcre support or remove mod_ssi from the list of modules"); return HANDLER_ERROR; #endif - + return HANDLER_GO_ON; } -int ssi_env_add(array *env, const char *key, const char *val) { +static int ssi_env_add(array *env, const char *key, const char *val) { data_string *ds; - + if (NULL == (ds = (data_string *)array_get_unused_element(env, TYPE_STRING))) { ds = data_string_init(); } buffer_copy_string(ds->key, key); buffer_copy_string(ds->value, val); - + array_insert_unique(env, (data_unset *)ds); - + return 0; } /** * * the next two functions are take from fcgi.c - * + * */ static int ssi_env_add_request_headers(server *srv, connection *con, plugin_data *p) { size_t i; - + for (i = 0; i < con->request.headers->used; i++) { data_string *ds; - + ds = (data_string *)con->request.headers->data[i]; - + if (ds->value->used && ds->key->used) { size_t j; buffer_reset(srv->tmp_buf); - + /* don't forward the Authorization: Header */ if (0 == strcasecmp(ds->key->ptr, "AUTHORIZATION")) { continue; } - + if (0 != strcasecmp(ds->key->ptr, "CONTENT-TYPE")) { - buffer_copy_string(srv->tmp_buf, "HTTP_"); + buffer_copy_string_len(srv->tmp_buf, CONST_STR_LEN("HTTP_")); srv->tmp_buf->used--; } - + buffer_prepare_append(srv->tmp_buf, ds->key->used + 2); for (j = 0; j < ds->key->used - 1; j++) { char c = '_'; @@ -189,33 +199,61 @@ static int ssi_env_add_request_headers(server *srv, connection *con, plugin_data srv->tmp_buf->ptr[srv->tmp_buf->used++] = c; } srv->tmp_buf->ptr[srv->tmp_buf->used] = '\0'; - + ssi_env_add(p->ssi_cgi_env, srv->tmp_buf->ptr, ds->value->ptr); } } - + + for (i = 0; i < con->environment->used; i++) { + data_string *ds; + + ds = (data_string *)con->environment->data[i]; + + if (ds->value->used && ds->key->used) { + size_t j; + + buffer_reset(srv->tmp_buf); + buffer_prepare_append(srv->tmp_buf, ds->key->used + 2); + + for (j = 0; j < ds->key->used - 1; j++) { + char c = '_'; + if (light_isalpha(ds->key->ptr[j])) { + /* upper-case */ + c = ds->key->ptr[j] & ~32; + } else if (light_isdigit(ds->key->ptr[j])) { + /* copy */ + c = ds->key->ptr[j]; + } + srv->tmp_buf->ptr[srv->tmp_buf->used++] = c; + } + srv->tmp_buf->ptr[srv->tmp_buf->used] = '\0'; + + ssi_env_add(p->ssi_cgi_env, srv->tmp_buf->ptr, ds->value->ptr); + } + } + return 0; } static int build_ssi_cgi_vars(server *srv, connection *con, plugin_data *p) { char buf[32]; - + server_socket *srv_sock = con->srv_socket; - + #ifdef HAVE_IPV6 char b2[INET6_ADDRSTRLEN + 1]; #endif #define CONST_STRING(x) \ x - + array_reset(p->ssi_cgi_env); - - ssi_env_add(p->ssi_cgi_env, CONST_STRING("SERVER_SOFTWARE"), PACKAGE_NAME"/"PACKAGE_VERSION); + + ssi_env_add(p->ssi_cgi_env, CONST_STRING("SERVER_SOFTWARE"), PACKAGE_DESC); ssi_env_add(p->ssi_cgi_env, CONST_STRING("SERVER_NAME"), #ifdef HAVE_IPV6 - inet_ntop(srv_sock->addr.plain.sa_family, - srv_sock->addr.plain.sa_family == AF_INET6 ? + inet_ntop(srv_sock->addr.plain.sa_family, + srv_sock->addr.plain.sa_family == AF_INET6 ? (const void *) &(srv_sock->addr.ipv6.sin6_addr) : (const void *) &(srv_sock->addr.ipv4.sin_addr), b2, sizeof(b2)-1) @@ -224,30 +262,30 @@ static int build_ssi_cgi_vars(server *srv, connection *con, plugin_data *p) { #endif ); ssi_env_add(p->ssi_cgi_env, CONST_STRING("GATEWAY_INTERFACE"), "CGI/1.1"); - - ltostr(buf, + + LI_ltostr(buf, #ifdef HAVE_IPV6 ntohs(srv_sock->addr.plain.sa_family ? srv_sock->addr.ipv6.sin6_port : srv_sock->addr.ipv4.sin_port) #else ntohs(srv_sock->addr.ipv4.sin_port) #endif ); - + ssi_env_add(p->ssi_cgi_env, CONST_STRING("SERVER_PORT"), buf); - + ssi_env_add(p->ssi_cgi_env, CONST_STRING("REMOTE_ADDR"), inet_ntop_cache_get_ip(srv, &(con->dst_addr))); - + if (con->authed_user->used) { ssi_env_add(p->ssi_cgi_env, CONST_STRING("REMOTE_USER"), con->authed_user->ptr); } - + if (con->request.content_length > 0) { /* CGI-SPEC 6.1.2 and FastCGI spec 6.3 */ - + /* request.content_length < SSIZE_MAX, see request.c */ - ltostr(buf, con->request.content_length); + LI_ltostr(buf, con->request.content_length); ssi_env_add(p->ssi_cgi_env, CONST_STRING("CONTENT_LENGTH"), buf); } @@ -271,30 +309,30 @@ static int build_ssi_cgi_vars(server *srv, connection *con, plugin_data *p) { if (con->request.pathinfo->used) { ssi_env_add(p->ssi_cgi_env, CONST_STRING("PATH_INFO"), con->request.pathinfo->ptr); } - + ssi_env_add(p->ssi_cgi_env, CONST_STRING("SCRIPT_FILENAME"), con->physical.path->ptr); ssi_env_add(p->ssi_cgi_env, CONST_STRING("DOCUMENT_ROOT"), con->physical.doc_root->ptr); - + ssi_env_add(p->ssi_cgi_env, CONST_STRING("REQUEST_URI"), con->request.uri->ptr); ssi_env_add(p->ssi_cgi_env, CONST_STRING("QUERY_STRING"), con->uri.query->used ? con->uri.query->ptr : ""); ssi_env_add(p->ssi_cgi_env, CONST_STRING("REQUEST_METHOD"), get_http_method_name(con->request.http_method)); ssi_env_add(p->ssi_cgi_env, CONST_STRING("REDIRECT_STATUS"), "200"); ssi_env_add(p->ssi_cgi_env, CONST_STRING("SERVER_PROTOCOL"), get_http_version_name(con->request.http_version)); - + ssi_env_add_request_headers(srv, con, p); - + return 0; } -static int process_ssi_stmt(server *srv, connection *con, plugin_data *p, +static int process_ssi_stmt(server *srv, connection *con, plugin_data *p, const char **l, size_t n) { size_t i, ssicmd = 0; char buf[255]; buffer *b = NULL; - - struct { + + struct { const char *var; - enum { SSI_UNSET, SSI_ECHO, SSI_FSIZE, SSI_INCLUDE, SSI_FLASTMOD, + enum { SSI_UNSET, SSI_ECHO, SSI_FSIZE, SSI_INCLUDE, SSI_FLASTMOD, SSI_CONFIG, SSI_PRINTENV, SSI_SET, SSI_IF, SSI_ELIF, SSI_ELSE, SSI_ENDIF, SSI_EXEC } type; } ssicmds[] = { @@ -310,27 +348,28 @@ static int process_ssi_stmt(server *srv, connection *con, plugin_data *p, { "endif", SSI_ENDIF }, { "else", SSI_ELSE }, { "exec", SSI_EXEC }, - + { NULL, SSI_UNSET } }; - + for (i = 0; ssicmds[i].var; i++) { if (0 == strcmp(l[1], ssicmds[i].var)) { ssicmd = ssicmds[i].type; break; } } - + switch(ssicmd) { case SSI_ECHO: { /* echo */ - int var = 0, enc = 0; + int var = 0; + /* int enc = 0; */ const char *var_val = NULL; stat_cache_entry *sce = NULL; - - struct { + + struct { const char *var; - enum { SSI_ECHO_UNSET, SSI_ECHO_DATE_GMT, SSI_ECHO_DATE_LOCAL, SSI_ECHO_DOCUMENT_NAME, SSI_ECHO_DOCUMENT_URI, + enum { SSI_ECHO_UNSET, SSI_ECHO_DATE_GMT, SSI_ECHO_DATE_LOCAL, SSI_ECHO_DOCUMENT_NAME, SSI_ECHO_DOCUMENT_URI, SSI_ECHO_LAST_MODIFIED, SSI_ECHO_USER_NAME } type; } echovars[] = { { "DATE_GMT", SSI_ECHO_DATE_GMT }, @@ -339,27 +378,29 @@ static int process_ssi_stmt(server *srv, connection *con, plugin_data *p, { "DOCUMENT_URI", SSI_ECHO_DOCUMENT_URI }, { "LAST_MODIFIED", SSI_ECHO_LAST_MODIFIED }, { "USER_NAME", SSI_ECHO_USER_NAME }, - + { NULL, SSI_ECHO_UNSET } }; - - struct { + +/* + struct { const char *var; enum { SSI_ENC_UNSET, SSI_ENC_URL, SSI_ENC_NONE, SSI_ENC_ENTITY } type; } encvars[] = { { "url", SSI_ENC_URL }, { "none", SSI_ENC_NONE }, { "entity", SSI_ENC_ENTITY }, - + { NULL, SSI_ENC_UNSET } }; - +*/ + for (i = 2; i < n; i += 2) { if (0 == strcmp(l[i], "var")) { int j; - + var_val = l[i+1]; - + for (j = 0; echovars[j].var; j++) { if (0 == strcmp(l[i+1], echovars[j].var)) { var = echovars[j].type; @@ -367,36 +408,38 @@ static int process_ssi_stmt(server *srv, connection *con, plugin_data *p, } } } else if (0 == strcmp(l[i], "encoding")) { +/* int j; - + for (j = 0; encvars[j].var; j++) { if (0 == strcmp(l[i+1], encvars[j].var)) { enc = encvars[j].type; break; } } +*/ } else { log_error_write(srv, __FILE__, __LINE__, "sss", - "ssi: unknow attribute for ", + "ssi: unknow attribute for ", l[1], l[i]); } } - + if (p->if_is_false) break; - + if (!var_val) { log_error_write(srv, __FILE__, __LINE__, "sss", - "ssi: ", + "ssi: ", l[1], "var is missing"); break; } stat_cache_get_entry(srv, con, con->physical.path, &sce); - + switch(var) { case SSI_ECHO_USER_NAME: { struct passwd *pw; - + b = chunkqueue_get_append_buffer(con->write_queue); #ifdef HAVE_PWD_H if (NULL == (pw = getpwuid(sce->st.st_uid))) { @@ -411,10 +454,10 @@ static int process_ssi_stmt(server *srv, connection *con, plugin_data *p, } case SSI_ECHO_LAST_MODIFIED: { time_t t = sce->st.st_mtime; - + b = chunkqueue_get_append_buffer(con->write_queue); if (0 == strftime(buf, sizeof(buf), p->timefmt->ptr, localtime(&t))) { - buffer_copy_string(b, "(none)"); + buffer_copy_string_len(b, CONST_STR_LEN("(none)")); } else { buffer_copy_string(b, buf); } @@ -422,10 +465,10 @@ static int process_ssi_stmt(server *srv, connection *con, plugin_data *p, } case SSI_ECHO_DATE_LOCAL: { time_t t = time(NULL); - + b = chunkqueue_get_append_buffer(con->write_queue); if (0 == strftime(buf, sizeof(buf), p->timefmt->ptr, localtime(&t))) { - buffer_copy_string(b, "(none)"); + buffer_copy_string_len(b, CONST_STR_LEN("(none)")); } else { buffer_copy_string(b, buf); } @@ -433,10 +476,10 @@ static int process_ssi_stmt(server *srv, connection *con, plugin_data *p, } case SSI_ECHO_DATE_GMT: { time_t t = time(NULL); - + b = chunkqueue_get_append_buffer(con->write_queue); if (0 == strftime(buf, sizeof(buf), p->timefmt->ptr, gmtime(&t))) { - buffer_copy_string(b, "(none)"); + buffer_copy_string_len(b, CONST_STR_LEN("(none)")); } else { buffer_copy_string(b, buf); } @@ -444,7 +487,7 @@ static int process_ssi_stmt(server *srv, connection *con, plugin_data *p, } case SSI_ECHO_DOCUMENT_NAME: { char *sl; - + b = chunkqueue_get_append_buffer(con->write_queue); if (NULL == (sl = strrchr(con->physical.path->ptr, '/'))) { buffer_copy_string_buffer(b, con->physical.path); @@ -461,15 +504,15 @@ static int process_ssi_stmt(server *srv, connection *con, plugin_data *p, default: { data_string *ds; /* check if it is a cgi-var */ - + b = chunkqueue_get_append_buffer(con->write_queue); - + if (NULL != (ds = (data_string *)array_get_element(p->ssi_cgi_env, var_val))) { buffer_copy_string_buffer(b, ds->value); } else { - buffer_copy_string(b, "(none)"); + buffer_copy_string_len(b, CONST_STR_LEN("(none)")); } - + break; } } @@ -481,7 +524,7 @@ static int process_ssi_stmt(server *srv, connection *con, plugin_data *p, const char * file_path = NULL, *virt_path = NULL; struct stat st; char *sl; - + for (i = 2; i < n; i += 2) { if (0 == strcmp(l[i], "file")) { file_path = l[i+1]; @@ -489,78 +532,76 @@ static int process_ssi_stmt(server *srv, connection *con, plugin_data *p, virt_path = l[i+1]; } else { log_error_write(srv, __FILE__, __LINE__, "sss", - "ssi: unknow attribute for ", + "ssi: unknow attribute for ", l[1], l[i]); } } - + if (!file_path && !virt_path) { log_error_write(srv, __FILE__, __LINE__, "sss", - "ssi: ", + "ssi: ", l[1], "file or virtual are missing"); break; } - + if (file_path && virt_path) { log_error_write(srv, __FILE__, __LINE__, "sss", - "ssi: ", + "ssi: ", l[1], "only one of file and virtual is allowed here"); break; } - - + + if (p->if_is_false) break; - + if (file_path) { /* current doc-root */ if (NULL == (sl = strrchr(con->physical.path->ptr, '/'))) { - buffer_copy_string(p->stat_fn, "/"); + buffer_copy_string_len(p->stat_fn, CONST_STR_LEN("/")); } else { buffer_copy_string_len(p->stat_fn, con->physical.path->ptr, sl - con->physical.path->ptr + 1); } - - /* fn */ - if (NULL == (sl = strrchr(file_path, '/'))) { - buffer_append_string(p->stat_fn, file_path); - } else { - buffer_append_string(p->stat_fn, sl + 1); - } + + buffer_copy_string(srv->tmp_buf, file_path); + buffer_urldecode_path(srv->tmp_buf); + buffer_path_simplify(srv->tmp_buf, srv->tmp_buf); + buffer_append_string_buffer(p->stat_fn, srv->tmp_buf); } else { /* virtual */ - + if (virt_path[0] == '/') { buffer_copy_string(p->stat_fn, virt_path); } else { /* there is always a / */ sl = strrchr(con->uri.path->ptr, '/'); - + buffer_copy_string_len(p->stat_fn, con->uri.path->ptr, sl - con->uri.path->ptr + 1); buffer_append_string(p->stat_fn, virt_path); } - + buffer_urldecode_path(p->stat_fn); buffer_path_simplify(srv->tmp_buf, p->stat_fn); - + /* we have an uri */ - + buffer_copy_string_buffer(p->stat_fn, con->physical.doc_root); buffer_append_string_buffer(p->stat_fn, srv->tmp_buf); } - + if (0 == stat(p->stat_fn->ptr, &st)) { time_t t = st.st_mtime; - + switch (ssicmd) { case SSI_FSIZE: b = chunkqueue_get_append_buffer(con->write_queue); if (p->sizefmt) { int j = 0; const char *abr[] = { " B", " kB", " MB", " GB", " TB", NULL }; - + off_t s = st.st_size; - + for (j = 0; s > 1024 && abr[j+1]; s /= 1024, j++); - + buffer_copy_off_t(b, s); buffer_append_string(b, abr[j]); } else { @@ -570,18 +611,23 @@ static int process_ssi_stmt(server *srv, connection *con, plugin_data *p, case SSI_FLASTMOD: b = chunkqueue_get_append_buffer(con->write_queue); if (0 == strftime(buf, sizeof(buf), p->timefmt->ptr, localtime(&t))) { - buffer_copy_string(b, "(none)"); + buffer_copy_string_len(b, CONST_STR_LEN("(none)")); } else { buffer_copy_string(b, buf); } break; case SSI_INCLUDE: chunkqueue_append_file(con->write_queue, p->stat_fn, 0, st.st_size); + + /* Keep the newest mtime of included files */ + if (st.st_mtime > include_file_last_mtime) + include_file_last_mtime = st.st_mtime; + break; } } else { log_error_write(srv, __FILE__, __LINE__, "sbs", - "ssi: stating failed ", + "ssi: stating failed ", p->stat_fn, strerror(errno)); } break; @@ -595,33 +641,33 @@ static int process_ssi_stmt(server *srv, connection *con, plugin_data *p, val = l[i+1]; } else { log_error_write(srv, __FILE__, __LINE__, "sss", - "ssi: unknow attribute for ", + "ssi: unknow attribute for ", l[1], l[i]); } } - + if (p->if_is_false) break; - + if (key && val) { data_string *ds; - + if (NULL == (ds = (data_string *)array_get_unused_element(p->ssi_vars, TYPE_STRING))) { ds = data_string_init(); } buffer_copy_string(ds->key, key); buffer_copy_string(ds->value, val); - + array_insert_unique(p->ssi_vars, (data_unset *)ds); } else { log_error_write(srv, __FILE__, __LINE__, "sss", - "ssi: var and value have to be set in", + "ssi: var and value have to be set in", l[0], l[1]); } break; } - case SSI_CONFIG: + case SSI_CONFIG: if (p->if_is_false) break; - + for (i = 2; i < n; i += 2) { if (0 == strcmp(l[i], "timefmt")) { buffer_copy_string(p->timefmt, l[i+1]); @@ -634,63 +680,68 @@ static int process_ssi_stmt(server *srv, connection *con, plugin_data *p, log_error_write(srv, __FILE__, __LINE__, "sssss", "ssi: unknow value for attribute '", l[i], - "' for ", + "' for ", l[1], l[i+1]); } } else { log_error_write(srv, __FILE__, __LINE__, "sss", - "ssi: unknow attribute for ", + "ssi: unknow attribute for ", l[1], l[i]); } } break; case SSI_PRINTENV: if (p->if_is_false) break; - + b = chunkqueue_get_append_buffer(con->write_queue); - buffer_copy_string(b, "<pre>"); for (i = 0; i < p->ssi_vars->used; i++) { data_string *ds = (data_string *)p->ssi_vars->data[p->ssi_vars->sorted[i]]; - + buffer_append_string_buffer(b, ds->key); - buffer_append_string(b, ": "); - buffer_append_string_buffer(b, ds->value); - buffer_append_string(b, "<br />"); - + buffer_append_string_len(b, CONST_STR_LEN("=")); + buffer_append_string_encoded(b, CONST_BUF_LEN(ds->value), ENCODING_MINIMAL_XML); + buffer_append_string_len(b, CONST_STR_LEN("\n")); } - buffer_append_string(b, "</pre>"); - + for (i = 0; i < p->ssi_cgi_env->used; i++) { + data_string *ds = (data_string *)p->ssi_cgi_env->data[p->ssi_cgi_env->sorted[i]]; + + buffer_append_string_buffer(b, ds->key); + buffer_append_string_len(b, CONST_STR_LEN("=")); + buffer_append_string_encoded(b, CONST_BUF_LEN(ds->value), ENCODING_MINIMAL_XML); + buffer_append_string_len(b, CONST_STR_LEN("\n")); + } + break; case SSI_EXEC: { const char *cmd = NULL; pid_t pid; int from_exec_fds[2]; - + for (i = 2; i < n; i += 2) { if (0 == strcmp(l[i], "cmd")) { cmd = l[i+1]; } else { log_error_write(srv, __FILE__, __LINE__, "sss", - "ssi: unknow attribute for ", + "ssi: unknow attribute for ", l[1], l[i]); } } - + if (p->if_is_false) break; - + /* create a return pipe and send output to the html-page - * - * as exec is assumed evil it is implemented synchronously + * + * as exec is assumed evil it is implemented synchronously */ - + if (!cmd) break; -#ifdef HAVE_FORK +#ifdef HAVE_FORK if (pipe(from_exec_fds)) { - log_error_write(srv, __FILE__, __LINE__, "ss", + log_error_write(srv, __FILE__, __LINE__, "ss", "pipe failed: ", strerror(errno)); return -1; } - + /* fork, execve */ switch (pid = fork()) { case 0: { @@ -700,14 +751,14 @@ static int process_ssi_stmt(server *srv, connection *con, plugin_data *p, close(from_exec_fds[1]); /* not needed */ close(from_exec_fds[0]); - + /* close stdin */ close(STDIN_FILENO); - - execl("/bin/sh", "sh", "-c", cmd, NULL); - + + execl("/bin/sh", "sh", "-c", cmd, (char *)NULL); + log_error_write(srv, __FILE__, __LINE__, "sss", "spawing exec failed:", strerror(errno), cmd); - + /* */ SEGFAULT(); break; @@ -720,44 +771,59 @@ static int process_ssi_stmt(server *srv, connection *con, plugin_data *p, /* father */ int status; ssize_t r; - - close(from_exec_fds[1]); - - /* wait for the client to end */ - if (-1 == waitpid(pid, &status, 0)) { - log_error_write(srv, __FILE__, __LINE__, "ss", "waitpid failed:", strerror(errno)); - } else if (WIFEXITED(status)) { - int toread; - /* read everything from client and paste it into the output */ - - while(1) { - if (ioctl(from_exec_fds[0], FIONREAD, &toread)) { - log_error_write(srv, __FILE__, __LINE__, "s", - "unexpected end-of-file (perhaps the ssi-exec process died)"); - return -1; - } + int was_interrupted = 0; - if (toread > 0) { - b = chunkqueue_get_append_buffer(con->write_queue); + close(from_exec_fds[1]); - buffer_prepare_copy(b, toread + 1); + /* wait for the client to end */ - if ((r = read(from_exec_fds[0], b->ptr, b->size - 1)) < 0) { - /* read failed */ - break; + /* + * OpenBSD and Solaris send a EINTR on SIGCHILD even if we ignore it + */ + do { + if (-1 == waitpid(pid, &status, 0)) { + if (errno == EINTR) { + was_interrupted++; + } else { + was_interrupted = 0; + log_error_write(srv, __FILE__, __LINE__, "ss", "waitpid failed:", strerror(errno)); + } + } else if (WIFEXITED(status)) { + int toread; + /* read everything from client and paste it into the output */ + was_interrupted = 0; + + while(1) { + if (ioctl(from_exec_fds[0], FIONREAD, &toread)) { + log_error_write(srv, __FILE__, __LINE__, "s", + "unexpected end-of-file (perhaps the ssi-exec process died)"); + return -1; + } + + if (toread > 0) { + b = chunkqueue_get_append_buffer(con->write_queue); + + buffer_prepare_copy(b, toread + 1); + + if ((r = read(from_exec_fds[0], b->ptr, b->size - 1)) < 0) { + /* read failed */ + break; + } else { + b->used = r; + b->ptr[b->used++] = '\0'; + } } else { - b->used = r; - b->ptr[b->used++] = '\0'; + break; } - } else { - break; } + } else { + was_interrupted = 0; + log_error_write(srv, __FILE__, __LINE__, "s", "process exited abnormally"); } - } else { - log_error_write(srv, __FILE__, __LINE__, "s", "process exited abnormally"); - } + } while (was_interrupted > 0 && was_interrupted < 4); /* if waitpid() gets interrupted, retry, but max 4 times */ + close(from_exec_fds[0]); - + break; } } @@ -765,51 +831,51 @@ static int process_ssi_stmt(server *srv, connection *con, plugin_data *p, return -1; #endif - + break; } case SSI_IF: { const char *expr = NULL; - + for (i = 2; i < n; i += 2) { if (0 == strcmp(l[i], "expr")) { expr = l[i+1]; } else { log_error_write(srv, __FILE__, __LINE__, "sss", - "ssi: unknow attribute for ", + "ssi: unknow attribute for ", l[1], l[i]); } } - + if (!expr) { log_error_write(srv, __FILE__, __LINE__, "sss", - "ssi: ", + "ssi: ", l[1], "expr missing"); break; } - + if ((!p->if_is_false) && - ((p->if_is_false_level == 0) || + ((p->if_is_false_level == 0) || (p->if_level < p->if_is_false_level))) { switch (ssi_eval_expr(srv, con, p, expr)) { case -1: - case 0: - p->if_is_false = 1; + case 0: + p->if_is_false = 1; p->if_is_false_level = p->if_level; break; - case 1: - p->if_is_false = 0; + case 1: + p->if_is_false = 0; break; } } - + p->if_level++; - + break; } case SSI_ELSE: p->if_level--; - + if (p->if_is_false) { if ((p->if_level == p->if_is_false_level) && (p->if_is_false_endif == 0)) { @@ -817,11 +883,11 @@ static int process_ssi_stmt(server *srv, connection *con, plugin_data *p, } } else { p->if_is_false = 1; - + p->if_is_false_level = p->if_level; } p->if_level++; - + break; case SSI_ELIF: { const char *expr = NULL; @@ -830,52 +896,52 @@ static int process_ssi_stmt(server *srv, connection *con, plugin_data *p, expr = l[i+1]; } else { log_error_write(srv, __FILE__, __LINE__, "sss", - "ssi: unknow attribute for ", + "ssi: unknow attribute for ", l[1], l[i]); } } - + if (!expr) { log_error_write(srv, __FILE__, __LINE__, "sss", - "ssi: ", + "ssi: ", l[1], "expr missing"); break; } - + p->if_level--; - + if (p->if_level == p->if_is_false_level) { if ((p->if_is_false) && (p->if_is_false_endif == 0)) { switch (ssi_eval_expr(srv, con, p, expr)) { case -1: - case 0: - p->if_is_false = 1; + case 0: + p->if_is_false = 1; p->if_is_false_level = p->if_level; break; - case 1: - p->if_is_false = 0; + case 1: + p->if_is_false = 0; break; } } else { - p->if_is_false = 1; + p->if_is_false = 1; p->if_is_false_level = p->if_level; p->if_is_false_endif = 1; } } - + p->if_level++; - + break; } case SSI_ENDIF: p->if_level--; - + if (p->if_level == p->if_is_false_level) { p->if_is_false = 0; p->if_is_false_endif = 0; } - + break; default: log_error_write(srv, __FILE__, __LINE__, "ss", @@ -883,41 +949,44 @@ static int process_ssi_stmt(server *srv, connection *con, plugin_data *p, l[1]); break; } - + return 0; - + } static int mod_ssi_handle_request(server *srv, connection *con, plugin_data *p) { stream s; #ifdef HAVE_PCRE_H int i, n; - + #define N 10 int ovec[N * 3]; #endif - + /* get a stream to the file */ - + array_reset(p->ssi_vars); array_reset(p->ssi_cgi_env); - buffer_copy_string(p->timefmt, "%a, %d %b %Y %H:%M:%S %Z"); + buffer_copy_string_len(p->timefmt, CONST_STR_LEN("%a, %d %b %Y %H:%M:%S %Z")); p->sizefmt = 0; build_ssi_cgi_vars(srv, con, p); p->if_is_false = 0; - + + /* Reset the modified time of included files */ + include_file_last_mtime = 0; + if (-1 == stream_open(&s, con->physical.path)) { log_error_write(srv, __FILE__, __LINE__, "sb", "stream-open: ", con->physical.path); return -1; } - - + + /** - * <!--#element attribute=value attribute=value ... --> - * + * <!--#element attribute=value attribute=value ... --> + * * config DONE - * errmsg -- missing + * errmsg -- missing * sizefmt DONE * timefmt DONE * echo DONE @@ -939,13 +1008,13 @@ static int mod_ssi_handle_request(server *srv, connection *con, plugin_data *p) * set DONE * var DONE * value DONE - * + * * if DONE * elif DONE * else DONE * endif DONE - * - * + * + * * expressions * AND, OR DONE * comp DONE @@ -953,60 +1022,89 @@ static int mod_ssi_handle_request(server *srv, connection *con, plugin_data *p) * $... DONE * '...' DONE * ( ... ) DONE - * - * - * + * + * + * * ** all DONE ** - * DATE_GMT - * The current date in Greenwich Mean Time. - * DATE_LOCAL - * The current date in the local time zone. - * DOCUMENT_NAME - * The filename (excluding directories) of the document requested by the user. - * DOCUMENT_URI - * The (%-decoded) URL path of the document requested by the user. Note that in the case of nested include files, this is not then URL for the current document. - * LAST_MODIFIED - * The last modification date of the document requested by the user. - * USER_NAME + * DATE_GMT + * The current date in Greenwich Mean Time. + * DATE_LOCAL + * The current date in the local time zone. + * DOCUMENT_NAME + * The filename (excluding directories) of the document requested by the user. + * DOCUMENT_URI + * The (%-decoded) URL path of the document requested by the user. Note that in the case of nested include files, this is not then URL for the current document. + * LAST_MODIFIED + * The last modification date of the document requested by the user. + * USER_NAME * Contains the owner of the file which included it. - * + * */ -#ifdef HAVE_PCRE_H +#ifdef HAVE_PCRE_H for (i = 0; (n = pcre_exec(p->ssi_regex, NULL, s.start, s.size, i, 0, ovec, N * 3)) > 0; i = ovec[1]) { const char **l; - /* take every think from last offset to current match pos */ - + /* take everything from last offset to current match pos */ + if (!p->if_is_false) chunkqueue_append_file(con->write_queue, con->physical.path, i, ovec[0] - i); - + pcre_get_substring_list(s.start, ovec, n, &l); process_ssi_stmt(srv, con, p, l, n); pcre_free_substring_list(l); } - + switch(n) { case PCRE_ERROR_NOMATCH: /* copy everything/the rest */ chunkqueue_append_file(con->write_queue, con->physical.path, i, s.size - i); - + break; default: log_error_write(srv, __FILE__, __LINE__, "sd", "execution error while matching: ", n); break; } -#endif - - +#endif + + stream_close(&s); - + con->file_started = 1; con->file_finished = 1; - - response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_STR_LEN("text/html")); - + con->mode = p->id; + + if (p->conf.content_type->used <= 1) { + response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_STR_LEN("text/html")); + } else { + response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_BUF_LEN(p->conf.content_type)); + } + + { + /* Generate "ETag" & "Last-Modified" headers */ + + stat_cache_entry *sce = NULL; + time_t lm_time = 0; + buffer *mtime = NULL; + + stat_cache_get_entry(srv, con, con->physical.path, &sce); + + etag_mutate(con->physical.etag, sce->etag); + response_header_overwrite(srv, con, CONST_STR_LEN("ETag"), CONST_BUF_LEN(con->physical.etag)); + + if (sce->st.st_mtime > include_file_last_mtime) + lm_time = sce->st.st_mtime; + else + lm_time = include_file_last_mtime; + + mtime = strftime_cache_get(srv, lm_time); + response_header_overwrite(srv, con, CONST_STR_LEN("Last-Modified"), CONST_BUF_LEN(mtime)); + } + + /* Reset the modified time of included files */ + include_file_last_mtime = 0; + /* reset physical.path */ buffer_reset(con->physical.path); - + return 0; } @@ -1015,27 +1113,30 @@ static int mod_ssi_handle_request(server *srv, connection *con, plugin_data *p) static int mod_ssi_patch_connection(server *srv, connection *con, plugin_data *p) { size_t i, j; plugin_config *s = p->config_storage[0]; - + PATCH(ssi_extension); - + PATCH(content_type); + /* skip the first, the global context */ for (i = 1; i < srv->config_context->used; i++) { data_config *dc = (data_config *)srv->config_context->data[i]; s = p->config_storage[i]; - + /* condition didn't match */ if (!config_check_cond(srv, con, dc)) continue; - + /* merge config */ for (j = 0; j < dc->value->used; j++) { data_unset *du = dc->value->data[j]; - + if (buffer_is_equal_string(du->key, CONST_STR_LEN("ssi.extension"))) { PATCH(ssi_extension); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("ssi.content-type"))) { + PATCH(content_type); } } } - + return 0; } #undef PATCH @@ -1043,44 +1144,48 @@ static int mod_ssi_patch_connection(server *srv, connection *con, plugin_data *p URIHANDLER_FUNC(mod_ssi_physical_path) { plugin_data *p = p_d; size_t k; - + + if (con->mode != DIRECT) return HANDLER_GO_ON; + if (con->physical.path->used == 0) return HANDLER_GO_ON; - + mod_ssi_patch_connection(srv, con, p); - + for (k = 0; k < p->conf.ssi_extension->used; k++) { data_string *ds = (data_string *)p->conf.ssi_extension->data[k]; - + if (ds->value->used == 0) continue; - + if (buffer_is_equal_right_len(con->physical.path, ds->value, ds->value->used - 1)) { /* handle ssi-request */ - + if (mod_ssi_handle_request(srv, con, p)) { /* on error */ con->http_status = 500; + con->mode = DIRECT; } - + return HANDLER_FINISHED; } } - + /* not found */ return HANDLER_GO_ON; } /* this function is called at dlopen() time and inits the callbacks */ +int mod_ssi_plugin_init(plugin *p); int mod_ssi_plugin_init(plugin *p) { p->version = LIGHTTPD_VERSION_ID; p->name = buffer_init_string("ssi"); - + p->init = mod_ssi_init; p->handle_subrequest_start = mod_ssi_physical_path; p->set_defaults = mod_ssi_set_defaults; p->cleanup = mod_ssi_free; - + p->data = NULL; - + return 0; } diff --git a/src/mod_ssi.h b/src/mod_ssi.h index 80f03ed..241e832 100644 --- a/src/mod_ssi.h +++ b/src/mod_ssi.h @@ -15,27 +15,28 @@ typedef struct { array *ssi_extension; + buffer *content_type; } plugin_config; typedef struct { PLUGIN_DATA; - -#ifdef HAVE_PCRE_H + +#ifdef HAVE_PCRE_H pcre *ssi_regex; -#endif +#endif buffer *timefmt; int sizefmt; - + buffer *stat_fn; - + array *ssi_vars; array *ssi_cgi_env; - + int if_level, if_is_false_level, if_is_false, if_is_false_endif; - + plugin_config **config_storage; - - plugin_config conf; + + plugin_config conf; } plugin_data; int ssi_eval_expr(server *srv, connection *con, plugin_data *p, const char *expr); diff --git a/src/mod_ssi_expr.c b/src/mod_ssi_expr.c index 98959ab..f839987 100644 --- a/src/mod_ssi_expr.c +++ b/src/mod_ssi_expr.c @@ -1,35 +1,35 @@ -#include <ctype.h> -#include <string.h> - #include "buffer.h" #include "log.h" #include "mod_ssi.h" #include "mod_ssi_expr.h" #include "mod_ssi_exprparser.h" +#include <ctype.h> +#include <string.h> + typedef struct { const char *input; size_t offset; size_t size; - + int line_pos; - + int in_key; int in_brace; int in_cond; } ssi_tokenizer_t; -ssi_val_t *ssi_val_init() { +ssi_val_t *ssi_val_init(void) { ssi_val_t *s; - + s = calloc(1, sizeof(*s)); - + return s; } void ssi_val_free(ssi_val_t *s) { if (s->str) buffer_free(s->str); - + free(s); } @@ -45,192 +45,192 @@ static int ssi_expr_tokenizer(server *srv, connection *con, plugin_data *p, ssi_tokenizer_t *t, int *token_id, buffer *token) { int tid = 0; size_t i; - + UNUSED(con); for (tid = 0; tid == 0 && t->offset < t->size && t->input[t->offset] ; ) { char c = t->input[t->offset]; data_string *ds; - + switch (c) { - case '=': + case '=': tid = TK_EQ; - + t->offset++; t->line_pos++; - - buffer_copy_string(token, "(=)"); - + + buffer_copy_string_len(token, CONST_STR_LEN("(=)")); + break; case '>': if (t->input[t->offset + 1] == '=') { t->offset += 2; t->line_pos += 2; - + tid = TK_GE; - - buffer_copy_string(token, "(>=)"); + + buffer_copy_string_len(token, CONST_STR_LEN("(>=)")); } else { t->offset += 1; t->line_pos += 1; - + tid = TK_GT; - - buffer_copy_string(token, "(>)"); + + buffer_copy_string_len(token, CONST_STR_LEN("(>)")); } - + break; case '<': if (t->input[t->offset + 1] == '=') { t->offset += 2; t->line_pos += 2; - + tid = TK_LE; - - buffer_copy_string(token, "(<=)"); + + buffer_copy_string_len(token, CONST_STR_LEN("(<=)")); } else { t->offset += 1; t->line_pos += 1; - + tid = TK_LT; - - buffer_copy_string(token, "(<)"); + + buffer_copy_string_len(token, CONST_STR_LEN("(<)")); } - + break; - + case '!': if (t->input[t->offset + 1] == '=') { t->offset += 2; t->line_pos += 2; - + tid = TK_NE; - - buffer_copy_string(token, "(!=)"); + + buffer_copy_string_len(token, CONST_STR_LEN("(!=)")); } else { t->offset += 1; t->line_pos += 1; - + tid = TK_NOT; - - buffer_copy_string(token, "(!)"); + + buffer_copy_string_len(token, CONST_STR_LEN("(!)")); } - + break; case '&': if (t->input[t->offset + 1] == '&') { t->offset += 2; t->line_pos += 2; - + tid = TK_AND; - - buffer_copy_string(token, "(&&)"); + + buffer_copy_string_len(token, CONST_STR_LEN("(&&)")); } else { - log_error_write(srv, __FILE__, __LINE__, "sds", - "pos:", t->line_pos, + log_error_write(srv, __FILE__, __LINE__, "sds", + "pos:", t->line_pos, "missing second &"); return -1; } - + break; case '|': if (t->input[t->offset + 1] == '|') { t->offset += 2; t->line_pos += 2; - + tid = TK_OR; - - buffer_copy_string(token, "(||)"); + + buffer_copy_string_len(token, CONST_STR_LEN("(||)")); } else { - log_error_write(srv, __FILE__, __LINE__, "sds", - "pos:", t->line_pos, + log_error_write(srv, __FILE__, __LINE__, "sds", + "pos:", t->line_pos, "missing second |"); return -1; } - + break; case '\t': case ' ': t->offset++; t->line_pos++; break; - + case '\'': /* search for the terminating " */ for (i = 1; t->input[t->offset + i] && t->input[t->offset + i] != '\''; i++); - + if (t->input[t->offset + i]) { tid = TK_VALUE; - + buffer_copy_string_len(token, t->input + t->offset + 1, i-1); - + t->offset += i + 1; t->line_pos += i + 1; } else { /* ERROR */ - - log_error_write(srv, __FILE__, __LINE__, "sds", - "pos:", t->line_pos, + + log_error_write(srv, __FILE__, __LINE__, "sds", + "pos:", t->line_pos, "missing closing quote"); - + return -1; } - + break; case '(': t->offset++; t->in_brace++; - + tid = TK_LPARAN; - - buffer_copy_string(token, "("); + + buffer_copy_string_len(token, CONST_STR_LEN("(")); break; case ')': t->offset++; t->in_brace--; - + tid = TK_RPARAN; - - buffer_copy_string(token, ")"); + + buffer_copy_string_len(token, CONST_STR_LEN(")")); break; case '$': if (t->input[t->offset + 1] == '{') { for (i = 2; t->input[t->offset + i] && t->input[t->offset + i] != '}'; i++); - + if (t->input[t->offset + i] != '}') { - log_error_write(srv, __FILE__, __LINE__, "sds", - "pos:", t->line_pos, + log_error_write(srv, __FILE__, __LINE__, "sds", + "pos:", t->line_pos, "missing closing quote"); - + return -1; } - + buffer_copy_string_len(token, t->input + t->offset + 2, i-3); } else { for (i = 1; isalpha(t->input[t->offset + i]) || t->input[t->offset + i] == '_'; i++); - + buffer_copy_string_len(token, t->input + t->offset + 1, i-1); } - + tid = TK_VALUE; - + if (NULL != (ds = (data_string *)array_get_element(p->ssi_cgi_env, token->ptr))) { buffer_copy_string_buffer(token, ds->value); } else if (NULL != (ds = (data_string *)array_get_element(p->ssi_vars, token->ptr))) { buffer_copy_string_buffer(token, ds->value); } else { - buffer_copy_string(token, ""); + buffer_copy_string_len(token, CONST_STR_LEN("")); } - + t->offset += i; t->line_pos += i; - + break; default: for (i = 0; isgraph(t->input[t->offset + i]); i++) { char d = t->input[t->offset + i]; switch(d) { - case ' ': + case ' ': case '\t': case ')': case '(': @@ -244,25 +244,25 @@ static int ssi_expr_tokenizer(server *srv, connection *con, plugin_data *p, break; } } - + tid = TK_VALUE; - + buffer_copy_string_len(token, t->input + t->offset, i); - + t->offset += i; t->line_pos += i; - + break; } } - + if (tid) { *token_id = tid; - + return 1; } else if (t->offset < t->size) { - log_error_write(srv, __FILE__, __LINE__, "sds", - "pos:", t->line_pos, + log_error_write(srv, __FILE__, __LINE__, "sds", + "pos:", t->line_pos, "foobar"); } return 0; @@ -275,50 +275,50 @@ int ssi_eval_expr(server *srv, connection *con, plugin_data *p, const char *expr buffer *token; ssi_ctx_t context; int ret; - + t.input = expr; t.offset = 0; t.size = strlen(expr); t.line_pos = 1; - + t.in_key = 1; t.in_brace = 0; t.in_cond = 0; - + context.ok = 1; context.srv = srv; - + /* default context */ - + pParser = ssiexprparserAlloc( malloc ); token = buffer_init(); while((1 == (ret = ssi_expr_tokenizer(srv, con, p, &t, &token_id, token))) && context.ok) { ssiexprparser(pParser, token_id, token, &context); - + token = buffer_init(); } ssiexprparser(pParser, 0, token, &context); ssiexprparserFree(pParser, free ); - + buffer_free(token); - + if (ret == -1) { - log_error_write(srv, __FILE__, __LINE__, "s", + log_error_write(srv, __FILE__, __LINE__, "s", "expr parser failed"); return -1; } - + if (context.ok == 0) { - log_error_write(srv, __FILE__, __LINE__, "sds", - "pos:", t.line_pos, + log_error_write(srv, __FILE__, __LINE__, "sds", + "pos:", t.line_pos, "parser failed somehow near here"); return -1; } #if 0 - log_error_write(srv, __FILE__, __LINE__, "ssd", + log_error_write(srv, __FILE__, __LINE__, "ssd", "expr: ", expr, context.val.bo); -#endif +#endif return context.val.bo; } diff --git a/src/mod_ssi_expr.h b/src/mod_ssi_expr.h index b484f78..2d3ae8b 100644 --- a/src/mod_ssi_expr.h +++ b/src/mod_ssi_expr.h @@ -5,16 +5,16 @@ typedef struct { enum { SSI_TYPE_UNSET, SSI_TYPE_BOOL, SSI_TYPE_STRING } type; - + buffer *str; int bo; } ssi_val_t; typedef struct { int ok; - + ssi_val_t val; - + void *srv; } ssi_ctx_t; diff --git a/src/mod_ssi_exprparser.c b/src/mod_ssi_exprparser.c index 65ec4dc..cd88eda 100644 --- a/src/mod_ssi_exprparser.c +++ b/src/mod_ssi_exprparser.c @@ -4,24 +4,22 @@ /* First off, code is include which follows the "include" declaration ** in the input file. */ #include <stdio.h> -#line 6 "./mod_ssi_exprparser.y" +#line 6 "../../src/mod_ssi_exprparser.y" -#include <assert.h> -#include <string.h> -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif #include "mod_ssi_expr.h" #include "buffer.h" -#line 18 "mod_ssi_exprparser.c" +#include <assert.h> +#include <string.h> + +#line 16 "mod_ssi_exprparser.c" /* Next is all token values, in a form suitable for use by makeheaders. ** This section will be null unless lemon is run with the -m switch. */ -/* +/* ** These constants (all generated automatically by the parser generator) ** specify the various kinds of tokens (terminals) that the parser -** understands. +** understands. ** ** Each symbol here is a terminal symbol in the grammar. */ @@ -38,7 +36,7 @@ ** and nonterminals. "int" is used otherwise. ** YYNOCODE is a number of type YYCODETYPE which corresponds ** to no legal terminal or nonterminal number. This -** number is used to fill in empty slots of the hash +** number is used to fill in empty slots of the hash ** table. ** YYFALLBACK If defined, this indicates that one or more tokens ** have fall-back values which should be used if the @@ -47,7 +45,7 @@ ** and nonterminal numbers. "unsigned char" is ** used if there are fewer than 250 rules and ** states combined. "int" is used otherwise. -** ssiexprparserTOKENTYPE is the data type used for minor tokens given +** ssiexprparserTOKENTYPE is the data type used for minor tokens given ** directly to the parser from the tokenizer. ** YYMINORTYPE is the data type used for all minor tokens. ** This is typically a union of many types, one of @@ -91,7 +89,7 @@ typedef union { /* Next are that tables used to determine what action to take based on the ** current state and lookahead token. These tables are used to implement ** functions that take a state number and lookahead value and return an -** action integer. +** action integer. ** ** Suppose the action integer is N. Then the action is determined as ** follows @@ -116,7 +114,7 @@ typedef union { ** If the index value yy_shift_ofst[S]+X is out of range or if the value ** yy_lookahead[yy_shift_ofst[S]+X] is not equal to X or if yy_shift_ofst[S] ** is equal to YY_SHIFT_USE_DFLT, it means that the action is not in the table -** and that yy_default[S] should be used instead. +** and that yy_default[S] should be used instead. ** ** The formula above is for computing the action when the lookahead is ** a terminal symbol. If the lookahead is a non-terminal (as occurs after @@ -168,7 +166,7 @@ static YYACTIONTYPE yy_default[] = { /* The next table maps tokens into fallback tokens. If a construct ** like the following: -** +** ** %fallback ID X Y Z. ** ** appears in the grammer, then ID becomes a fallback token for X, Y, @@ -214,15 +212,15 @@ typedef struct yyParser yyParser; #ifndef NDEBUG #include <stdio.h> -static FILE *yyTraceFILE = 0; -static char *yyTracePrompt = 0; +static FILE *yyTraceFILE = NULL; +static char *yyTracePrompt = NULL; #endif /* NDEBUG */ #ifndef NDEBUG -/* +/* ** Turn parser tracing on by giving a stream to which to write the trace ** and a prompt to preface each trace message. Tracing is turned off -** by making either argument NULL +** by making either argument NULL ** ** Inputs: ** <ul> @@ -236,18 +234,20 @@ static char *yyTracePrompt = 0; ** Outputs: ** None. */ +#if 0 void ssiexprparserTrace(FILE *TraceFILE, char *zTracePrompt){ yyTraceFILE = TraceFILE; yyTracePrompt = zTracePrompt; if( yyTraceFILE==0 ) yyTracePrompt = 0; else if( yyTracePrompt==0 ) yyTraceFILE = 0; } +#endif #endif /* NDEBUG */ #ifndef NDEBUG /* For tracing shifts, the names of all terminals and nonterminals ** are required. The following table supplies these names */ -static const char *yyTokenName[] = { +static const char *yyTokenName[] = { "$", "AND", "OR", "EQ", "NE", "GT", "GE", "LT", "LE", "NOT", "LPARAN", "RPARAN", @@ -283,9 +283,10 @@ static const char *yyRuleName[] = { ** This function returns the symbolic name associated with a token ** value. */ +#if 0 const char *ssiexprparserTokenName(int tokenType){ #ifndef NDEBUG - if( tokenType>0 && tokenType<(sizeof(yyTokenName)/sizeof(yyTokenName[0])) ){ + if( tokenType>0 && (size_t)tokenType<(sizeof(yyTokenName)/sizeof(yyTokenName[0])) ){ return yyTokenName[tokenType]; }else{ return "Unknown"; @@ -294,8 +295,9 @@ const char *ssiexprparserTokenName(int tokenType){ return ""; #endif } +#endif -/* +/* ** This function allocates a new parser. ** The only argument is a pointer to a function which works like ** malloc. @@ -326,7 +328,7 @@ static void yy_destructor(YYCODETYPE yymajor, YYMINORTYPE *yypminor){ /* Here is inserted the actions which take place when a ** terminal or non-terminal is destroyed. This can happen ** when the symbol is popped from the stack during a - ** reduce or during error processing or when a parser is + ** reduce or during error processing or when a parser is ** being destroyed before it is finished parsing. ** ** Note: during a reduce, the only symbols destroyed are those @@ -345,9 +347,9 @@ static void yy_destructor(YYCODETYPE yymajor, YYMINORTYPE *yypminor){ case 10: case 11: case 12: -#line 24 "./mod_ssi_exprparser.y" +#line 22 "../../src/mod_ssi_exprparser.y" { buffer_free((yypminor->yy0)); } -#line 350 "mod_ssi_exprparser.c" +#line 352 "mod_ssi_exprparser.c" break; default: break; /* If no destructor action specified: do nothing */ } @@ -379,7 +381,7 @@ static int yy_pop_parser_stack(yyParser *pParser){ return yymajor; } -/* +/* ** Deallocate and destroy a parser. Destructors are all called for ** all stack elements before shutting the parser down. ** @@ -396,7 +398,7 @@ void ssiexprparserFree( void (*freeProc)(void*) /* Function used to reclaim memory */ ){ yyParser *pParser = (yyParser*)p; - if( pParser==0 ) return; + if( pParser==NULL ) return; while( pParser->yyidx>=0 ) yy_pop_parser_stack(pParser); (*freeProc)((void*)pParser); } @@ -415,7 +417,7 @@ static int yy_find_shift_action( ){ int i; int stateno = pParser->yystack[pParser->yyidx].stateno; - + /* if( pParser->yyidx<0 ) return YY_NO_ACTION; */ i = yy_shift_ofst[stateno]; if( i==YY_SHIFT_USE_DFLT ){ @@ -425,7 +427,7 @@ static int yy_find_shift_action( return YY_NO_ACTION; } i += iLookAhead; - if( i<0 || i>=YY_SZ_ACTTAB || yy_lookahead[i]!=iLookAhead ){ + if( i<0 || (size_t)i>=YY_SZ_ACTTAB || yy_lookahead[i]!=iLookAhead ){ #ifdef YYFALLBACK int iFallback; /* Fallback token */ if( iLookAhead<sizeof(yyFallback)/sizeof(yyFallback[0]) @@ -459,7 +461,7 @@ static int yy_find_reduce_action( ){ int i; int stateno = pParser->yystack[pParser->yyidx].stateno; - + i = yy_reduce_ofst[stateno]; if( i==YY_REDUCE_USE_DFLT ){ return yy_default[stateno]; @@ -468,7 +470,7 @@ static int yy_find_reduce_action( return YY_NO_ACTION; } i += iLookAhead; - if( i<0 || i>=YY_SZ_ACTTAB || yy_lookahead[i]!=iLookAhead ){ + if( i<0 || (size_t)i>=YY_SZ_ACTTAB || yy_lookahead[i]!=iLookAhead ){ return yy_default[stateno]; }else{ return yy_action[i]; @@ -559,8 +561,8 @@ static void yy_reduce( ssiexprparserARG_FETCH; yymsp = &yypParser->yystack[yypParser->yyidx]; #ifndef NDEBUG - if( yyTraceFILE && yyruleno>=0 - && yyruleno<sizeof(yyRuleName)/sizeof(yyRuleName[0]) ){ + if( yyTraceFILE && yyruleno>=0 + && (size_t)yyruleno<sizeof(yyRuleName)/sizeof(yyRuleName[0]) ){ fprintf(yyTraceFILE, "%sReduce [%s].\n", yyTracePrompt, yyRuleName[yyruleno]); } @@ -576,29 +578,29 @@ static void yy_reduce( ** break; */ case 0: -#line 31 "./mod_ssi_exprparser.y" +#line 29 "../../src/mod_ssi_exprparser.y" { ctx->val.bo = ssi_val_tobool(yymsp[0].minor.yy29); ctx->val.type = SSI_TYPE_BOOL; - + ssi_val_free(yymsp[0].minor.yy29); } -#line 586 "mod_ssi_exprparser.c" +#line 588 "mod_ssi_exprparser.c" break; case 1: -#line 38 "./mod_ssi_exprparser.y" +#line 36 "../../src/mod_ssi_exprparser.y" { int cmp; - - if (yymsp[-2].minor.yy29->type == SSI_TYPE_STRING && + + if (yymsp[-2].minor.yy29->type == SSI_TYPE_STRING && yymsp[0].minor.yy29->type == SSI_TYPE_STRING) { cmp = strcmp(yymsp[-2].minor.yy29->str->ptr, yymsp[0].minor.yy29->str->ptr); } else { cmp = ssi_val_tobool(yymsp[-2].minor.yy29) - ssi_val_tobool(yymsp[0].minor.yy29); } - + yygotominor.yy29 = yymsp[-2].minor.yy29; - + switch(yymsp[-1].minor.yy8) { case SSI_COND_EQ: yygotominor.yy29->bo = (cmp == 0) ? 1 : 0; break; case SSI_COND_NE: yygotominor.yy29->bo = (cmp != 0) ? 1 : 0; break; @@ -607,131 +609,132 @@ static void yy_reduce( case SSI_COND_LE: yygotominor.yy29->bo = (cmp <= 0) ? 1 : 0; break; case SSI_COND_LT: yygotominor.yy29->bo = (cmp < 0) ? 1 : 0; break; } - + yygotominor.yy29->type = SSI_TYPE_BOOL; - + ssi_val_free(yymsp[0].minor.yy29); } -#line 615 "mod_ssi_exprparser.c" +#line 617 "mod_ssi_exprparser.c" break; case 2: -#line 63 "./mod_ssi_exprparser.y" +#line 61 "../../src/mod_ssi_exprparser.y" { yygotominor.yy29 = yymsp[0].minor.yy29; } -#line 622 "mod_ssi_exprparser.c" +#line 624 "mod_ssi_exprparser.c" break; case 3: -#line 66 "./mod_ssi_exprparser.y" +#line 64 "../../src/mod_ssi_exprparser.y" { int e; - + e = ssi_val_tobool(yymsp[-2].minor.yy29) && ssi_val_tobool(yymsp[0].minor.yy29); - + yygotominor.yy29 = yymsp[-2].minor.yy29; yygotominor.yy29->bo = e; yygotominor.yy29->type = SSI_TYPE_BOOL; ssi_val_free(yymsp[0].minor.yy29); } -#line 636 "mod_ssi_exprparser.c" +#line 638 "mod_ssi_exprparser.c" yy_destructor(1,&yymsp[-1].minor); break; case 4: -#line 77 "./mod_ssi_exprparser.y" +#line 75 "../../src/mod_ssi_exprparser.y" { int e; - + e = ssi_val_tobool(yymsp[-2].minor.yy29) || ssi_val_tobool(yymsp[0].minor.yy29); - + yygotominor.yy29 = yymsp[-2].minor.yy29; yygotominor.yy29->bo = e; yygotominor.yy29->type = SSI_TYPE_BOOL; ssi_val_free(yymsp[0].minor.yy29); } -#line 651 "mod_ssi_exprparser.c" +#line 653 "mod_ssi_exprparser.c" yy_destructor(2,&yymsp[-1].minor); break; case 5: -#line 88 "./mod_ssi_exprparser.y" +#line 86 "../../src/mod_ssi_exprparser.y" { int e; - + e = !ssi_val_tobool(yymsp[0].minor.yy29); - + yygotominor.yy29 = yymsp[0].minor.yy29; yygotominor.yy29->bo = e; yygotominor.yy29->type = SSI_TYPE_BOOL; } -#line 665 "mod_ssi_exprparser.c" +#line 667 "mod_ssi_exprparser.c" yy_destructor(9,&yymsp[-1].minor); break; case 6: -#line 97 "./mod_ssi_exprparser.y" +#line 95 "../../src/mod_ssi_exprparser.y" { yygotominor.yy29 = yymsp[-1].minor.yy29; } -#line 673 "mod_ssi_exprparser.c" +#line 675 "mod_ssi_exprparser.c" yy_destructor(10,&yymsp[-2].minor); yy_destructor(11,&yymsp[0].minor); break; case 7: -#line 101 "./mod_ssi_exprparser.y" +#line 99 "../../src/mod_ssi_exprparser.y" { yygotominor.yy29 = ssi_val_init(); yygotominor.yy29->str = yymsp[0].minor.yy19; yygotominor.yy29->type = SSI_TYPE_STRING; } -#line 684 "mod_ssi_exprparser.c" +#line 686 "mod_ssi_exprparser.c" break; case 8: -#line 107 "./mod_ssi_exprparser.y" +#line 105 "../../src/mod_ssi_exprparser.y" { - yygotominor.yy19 = buffer_init_string(yymsp[0].minor.yy0->ptr); + yygotominor.yy19 = yymsp[0].minor.yy0; } -#line 691 "mod_ssi_exprparser.c" +#line 693 "mod_ssi_exprparser.c" break; case 9: -#line 111 "./mod_ssi_exprparser.y" +#line 109 "../../src/mod_ssi_exprparser.y" { yygotominor.yy19 = yymsp[-1].minor.yy19; buffer_append_string_buffer(yygotominor.yy19, yymsp[0].minor.yy0); + buffer_free(yymsp[0].minor.yy0); } -#line 699 "mod_ssi_exprparser.c" +#line 702 "mod_ssi_exprparser.c" break; case 10: -#line 116 "./mod_ssi_exprparser.y" +#line 115 "../../src/mod_ssi_exprparser.y" { yygotominor.yy8 = SSI_COND_EQ; } -#line 704 "mod_ssi_exprparser.c" +#line 707 "mod_ssi_exprparser.c" yy_destructor(3,&yymsp[0].minor); break; case 11: -#line 117 "./mod_ssi_exprparser.y" +#line 116 "../../src/mod_ssi_exprparser.y" { yygotominor.yy8 = SSI_COND_NE; } -#line 710 "mod_ssi_exprparser.c" +#line 713 "mod_ssi_exprparser.c" yy_destructor(4,&yymsp[0].minor); break; case 12: -#line 118 "./mod_ssi_exprparser.y" +#line 117 "../../src/mod_ssi_exprparser.y" { yygotominor.yy8 = SSI_COND_LE; } -#line 716 "mod_ssi_exprparser.c" +#line 719 "mod_ssi_exprparser.c" yy_destructor(8,&yymsp[0].minor); break; case 13: -#line 119 "./mod_ssi_exprparser.y" +#line 118 "../../src/mod_ssi_exprparser.y" { yygotominor.yy8 = SSI_COND_GE; } -#line 722 "mod_ssi_exprparser.c" +#line 725 "mod_ssi_exprparser.c" yy_destructor(6,&yymsp[0].minor); break; case 14: -#line 120 "./mod_ssi_exprparser.y" +#line 119 "../../src/mod_ssi_exprparser.y" { yygotominor.yy8 = SSI_COND_LT; } -#line 728 "mod_ssi_exprparser.c" +#line 731 "mod_ssi_exprparser.c" yy_destructor(7,&yymsp[0].minor); break; case 15: -#line 121 "./mod_ssi_exprparser.y" +#line 120 "../../src/mod_ssi_exprparser.y" { yygotominor.yy8 = SSI_COND_GT; } -#line 734 "mod_ssi_exprparser.c" +#line 737 "mod_ssi_exprparser.c" yy_destructor(5,&yymsp[0].minor); break; }; @@ -761,11 +764,11 @@ static void yy_parse_failed( while( yypParser->yyidx>=0 ) yy_pop_parser_stack(yypParser); /* Here code is inserted which will be executed whenever the ** parser fails */ -#line 16 "./mod_ssi_exprparser.y" +#line 14 "../../src/mod_ssi_exprparser.y" ctx->ok = 0; -#line 768 "mod_ssi_exprparser.c" +#line 771 "mod_ssi_exprparser.c" ssiexprparserARG_STORE; /* Suppress warning about unused %extra_argument variable */ } @@ -778,6 +781,8 @@ static void yy_syntax_error( YYMINORTYPE yyminor /* The minor type of the error token */ ){ ssiexprparserARG_FETCH; + UNUSED(yymajor); + UNUSED(yyminor); #define TOKEN (yyminor.yy0) ssiexprparserARG_STORE; /* Suppress warning about unused %extra_argument variable */ } @@ -872,7 +877,7 @@ void ssiexprparser( #ifdef YYERRORSYMBOL /* A syntax error has occurred. ** The response to an error depends upon whether or not the - ** grammar defines an error token "ERROR". + ** grammar defines an error token "ERROR". ** ** This is what we do if the grammar does define ERROR: ** diff --git a/src/mod_ssi_exprparser.y b/src/mod_ssi_exprparser.y index c123941..f4aaa96 100644 --- a/src/mod_ssi_exprparser.y +++ b/src/mod_ssi_exprparser.y @@ -4,13 +4,11 @@ %name ssiexprparser %include { -#include <assert.h> -#include <string.h> -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif #include "mod_ssi_expr.h" #include "buffer.h" + +#include <assert.h> +#include <string.h> } %parse_failure { @@ -31,22 +29,22 @@ input ::= exprline(B). { ctx->val.bo = ssi_val_tobool(B); ctx->val.type = SSI_TYPE_BOOL; - + ssi_val_free(B); } exprline(A) ::= expr(B) cond(C) expr(D). { int cmp; - - if (B->type == SSI_TYPE_STRING && + + if (B->type == SSI_TYPE_STRING && D->type == SSI_TYPE_STRING) { cmp = strcmp(B->str->ptr, D->str->ptr); } else { cmp = ssi_val_tobool(B) - ssi_val_tobool(D); } - + A = B; - + switch(C) { case SSI_COND_EQ: A->bo = (cmp == 0) ? 1 : 0; break; case SSI_COND_NE: A->bo = (cmp != 0) ? 1 : 0; break; @@ -55,9 +53,9 @@ exprline(A) ::= expr(B) cond(C) expr(D). { case SSI_COND_LE: A->bo = (cmp <= 0) ? 1 : 0; break; case SSI_COND_LT: A->bo = (cmp < 0) ? 1 : 0; break; } - + A->type = SSI_TYPE_BOOL; - + ssi_val_free(D); } exprline(A) ::= expr(B). { @@ -65,9 +63,9 @@ exprline(A) ::= expr(B). { } expr(A) ::= expr(B) AND expr(C). { int e; - + e = ssi_val_tobool(B) && ssi_val_tobool(C); - + A = B; A->bo = e; A->type = SSI_TYPE_BOOL; @@ -76,9 +74,9 @@ expr(A) ::= expr(B) AND expr(C). { expr(A) ::= expr(B) OR expr(C). { int e; - + e = ssi_val_tobool(B) || ssi_val_tobool(C); - + A = B; A->bo = e; A->type = SSI_TYPE_BOOL; @@ -87,9 +85,9 @@ expr(A) ::= expr(B) OR expr(C). { expr(A) ::= NOT expr(B). { int e; - + e = !ssi_val_tobool(B); - + A = B; A->bo = e; A->type = SSI_TYPE_BOOL; @@ -105,12 +103,13 @@ expr(A) ::= value(B). { } value(A) ::= VALUE(B). { - A = buffer_init_string(B->ptr); + A = B; } value(A) ::= value(B) VALUE(C). { A = B; buffer_append_string_buffer(A, C); + buffer_free(C); } cond(A) ::= EQ. { A = SSI_COND_EQ; } diff --git a/src/mod_staticfile.c b/src/mod_staticfile.c index cbc443d..f5114dd 100644 --- a/src/mod_staticfile.c +++ b/src/mod_staticfile.c @@ -1,8 +1,3 @@ -#include <ctype.h> -#include <stdlib.h> -#include <stdio.h> -#include <string.h> - #include "base.h" #include "log.h" #include "buffer.h" @@ -14,9 +9,14 @@ #include "http_chunk.h" #include "response.h" +#include <ctype.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + /** * this is a staticfile for a lighttpd plugin - * + * */ @@ -25,52 +25,54 @@ typedef struct { array *exclude_ext; + unsigned short etags_used; + unsigned short disable_pathinfo; } plugin_config; typedef struct { PLUGIN_DATA; - + buffer *range_buf; - + plugin_config **config_storage; - - plugin_config conf; + + plugin_config conf; } plugin_data; /* init the plugin data */ INIT_FUNC(mod_staticfile_init) { plugin_data *p; - + p = calloc(1, sizeof(*p)); - + p->range_buf = buffer_init(); - + return p; } /* detroy the plugin data */ FREE_FUNC(mod_staticfile_free) { plugin_data *p = p_d; - + UNUSED(srv); if (!p) return HANDLER_GO_ON; - + if (p->config_storage) { size_t i; for (i = 0; i < srv->config_context->used; i++) { plugin_config *s = p->config_storage[i]; - + array_free(s->exclude_ext); - + free(s); } free(p->config_storage); } buffer_free(p->range_buf); - + free(p); - + return HANDLER_GO_ON; } @@ -79,31 +81,37 @@ FREE_FUNC(mod_staticfile_free) { SETDEFAULTS_FUNC(mod_staticfile_set_defaults) { plugin_data *p = p_d; size_t i = 0; - - config_values_t cv[] = { + + config_values_t cv[] = { { "static-file.exclude-extensions", NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION }, /* 0 */ + { "static-file.etags", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 1 */ + { "static-file.disable-pathinfo", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 2 */ { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET } }; - + if (!p) return HANDLER_ERROR; - + p->config_storage = calloc(1, srv->config_context->used * sizeof(specific_config *)); - + for (i = 0; i < srv->config_context->used; i++) { plugin_config *s; - + s = calloc(1, sizeof(plugin_config)); s->exclude_ext = array_init(); - + s->etags_used = 1; + s->disable_pathinfo = 0; + cv[0].destination = s->exclude_ext; - + cv[1].destination = &(s->etags_used); + cv[2].destination = &(s->disable_pathinfo); + p->config_storage[i] = s; - + if (0 != config_insert_values_global(srv, ((data_config *)srv->config_context->data[i])->value, cv)) { return HANDLER_ERROR; } } - + return HANDLER_GO_ON; } @@ -112,27 +120,33 @@ SETDEFAULTS_FUNC(mod_staticfile_set_defaults) { static int mod_staticfile_patch_connection(server *srv, connection *con, plugin_data *p) { size_t i, j; plugin_config *s = p->config_storage[0]; - + PATCH(exclude_ext); - + PATCH(etags_used); + PATCH(disable_pathinfo); + /* skip the first, the global context */ for (i = 1; i < srv->config_context->used; i++) { data_config *dc = (data_config *)srv->config_context->data[i]; s = p->config_storage[i]; - + /* condition didn't match */ if (!config_check_cond(srv, con, dc)) continue; - + /* merge config */ for (j = 0; j < dc->value->used; j++) { data_unset *du = dc->value->data[j]; - + if (buffer_is_equal_string(du->key, CONST_STR_LEN("static-file.exclude-extensions"))) { PATCH(exclude_ext); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("static-file.etags"))) { + PATCH(etags_used); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("static-file.disable-pathinfo"))) { + PATCH(disable_pathinfo); } } } - + return 0; } #undef PATCH @@ -146,69 +160,69 @@ static int http_response_parse_range(server *srv, connection *con, plugin_data * data_string *ds; stat_cache_entry *sce = NULL; buffer *content_type = NULL; - + if (HANDLER_ERROR == stat_cache_get_entry(srv, con, con->physical.path, &sce)) { SEGFAULT(); } - + start = 0; end = sce->st.st_size - 1; - + con->response.content_length = 0; - + if (NULL != (ds = (data_string *)array_get_element(con->response.headers, "Content-Type"))) { content_type = ds->value; } - + for (s = con->request.http_range, error = 0; !error && *s && NULL != (minus = strchr(s, '-')); ) { char *err; off_t la, le; - + if (s == minus) { /* -<stop> */ - + le = strtoll(s, &err, 10); - + if (le == 0) { /* RFC 2616 - 14.35.1 */ - + con->http_status = 416; error = 1; } else if (*err == '\0') { /* end */ s = err; - + end = sce->st.st_size - 1; start = sce->st.st_size + le; } else if (*err == ',') { multipart = 1; s = err + 1; - + end = sce->st.st_size - 1; start = sce->st.st_size + le; } else { error = 1; } - + } else if (*(minus+1) == '\0' || *(minus+1) == ',') { /* <start>- */ - + la = strtoll(s, &err, 10); - + if (err == minus) { /* ok */ - + if (*(err + 1) == '\0') { s = err + 1; - + end = sce->st.st_size - 1; start = la; - + } else if (*(err + 1) == ',') { multipart = 1; s = err + 2; - + end = sce->st.st_size - 1; start = la; } else { @@ -220,119 +234,119 @@ static int http_response_parse_range(server *srv, connection *con, plugin_data * } } else { /* <start>-<stop> */ - + la = strtoll(s, &err, 10); - + if (err == minus) { le = strtoll(minus+1, &err, 10); - + /* RFC 2616 - 14.35.1 */ if (la > le) { error = 1; } - + if (*err == '\0') { /* ok, end*/ s = err; - + end = le; start = la; } else if (*err == ',') { multipart = 1; s = err + 1; - + end = le; start = la; } else { /* error */ - + error = 1; } } else { /* error */ - + error = 1; } } - + if (!error) { if (start < 0) start = 0; - + /* RFC 2616 - 14.35.1 */ if (end > sce->st.st_size - 1) end = sce->st.st_size - 1; - + if (start > sce->st.st_size - 1) { error = 1; - + con->http_status = 416; } } - + if (!error) { if (multipart) { /* write boundary-header */ buffer *b; - + b = chunkqueue_get_append_buffer(con->write_queue); - - buffer_copy_string(b, "\r\n--"); + + buffer_copy_string_len(b, CONST_STR_LEN("\r\n--")); buffer_append_string(b, boundary); - + /* write Content-Range */ - buffer_append_string(b, "\r\nContent-Range: bytes "); + buffer_append_string_len(b, CONST_STR_LEN("\r\nContent-Range: bytes ")); buffer_append_off_t(b, start); - buffer_append_string(b, "-"); + buffer_append_string_len(b, CONST_STR_LEN("-")); buffer_append_off_t(b, end); - buffer_append_string(b, "/"); + buffer_append_string_len(b, CONST_STR_LEN("/")); buffer_append_off_t(b, sce->st.st_size); - - buffer_append_string(b, "\r\nContent-Type: "); + + buffer_append_string_len(b, CONST_STR_LEN("\r\nContent-Type: ")); buffer_append_string_buffer(b, content_type); - + /* write END-OF-HEADER */ - buffer_append_string(b, "\r\n\r\n"); - + buffer_append_string_len(b, CONST_STR_LEN("\r\n\r\n")); + con->response.content_length += b->used - 1; - + } - + chunkqueue_append_file(con->write_queue, con->physical.path, start, end - start + 1); con->response.content_length += end - start + 1; } } - + /* something went wrong */ if (error) return -1; - + if (multipart) { /* add boundary end */ buffer *b; - + b = chunkqueue_get_append_buffer(con->write_queue); - + buffer_copy_string_len(b, "\r\n--", 4); buffer_append_string(b, boundary); buffer_append_string_len(b, "--\r\n", 4); - + con->response.content_length += b->used - 1; - + /* set header-fields */ - - buffer_copy_string(p->range_buf, "multipart/byteranges; boundary="); + + buffer_copy_string_len(p->range_buf, CONST_STR_LEN("multipart/byteranges; boundary=")); buffer_append_string(p->range_buf, boundary); - + /* overwrite content-type */ response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_BUF_LEN(p->range_buf)); } else { /* add Content-Range-header */ - - buffer_copy_string(p->range_buf, "bytes "); + + buffer_copy_string_len(p->range_buf, CONST_STR_LEN("bytes ")); buffer_append_off_t(p->range_buf, start); - buffer_append_string(p->range_buf, "-"); + buffer_append_string_len(p->range_buf, CONST_STR_LEN("-")); buffer_append_off_t(p->range_buf, end); - buffer_append_string(p->range_buf, "/"); + buffer_append_string_len(p->range_buf, CONST_STR_LEN("/")); buffer_append_off_t(p->range_buf, sce->st.st_size); - + response_header_insert(srv, con, CONST_STR_LEN("Content-Range"), CONST_BUF_LEN(p->range_buf)); } @@ -343,16 +357,16 @@ static int http_response_parse_range(server *srv, connection *con, plugin_data * URIHANDLER_FUNC(mod_staticfile_subrequest) { plugin_data *p = p_d; size_t k; - int s_len; stat_cache_entry *sce = NULL; - buffer *mtime; + buffer *mtime = NULL; data_string *ds; - + int allow_caching = 1; + /* someone else has done a decision for us */ if (con->http_status != 0) return HANDLER_GO_ON; if (con->uri.path->used == 0) return HANDLER_GO_ON; if (con->physical.path->used == 0) return HANDLER_GO_ON; - + /* someone else has handled this request */ if (con->mode != DIRECT) return HANDLER_GO_ON; @@ -365,128 +379,184 @@ URIHANDLER_FUNC(mod_staticfile_subrequest) { default: return HANDLER_GO_ON; } - + mod_staticfile_patch_connection(srv, con, p); - - s_len = con->uri.path->used - 1; - + + if (p->conf.disable_pathinfo && 0 != con->request.pathinfo->used) { + if (con->conf.log_request_handling) { + log_error_write(srv, __FILE__, __LINE__, "s", "-- NOT handling file as static file, pathinfo forbidden"); + } + return HANDLER_GO_ON; + } + /* ignore certain extensions */ for (k = 0; k < p->conf.exclude_ext->used; k++) { - ds = (data_string *)p->conf.exclude_ext->data[k]; - + ds = (data_string *)p->conf.exclude_ext->data[k]; + if (ds->value->used == 0) continue; if (buffer_is_equal_right_len(con->physical.path, ds->value, ds->value->used - 1)) { + if (con->conf.log_request_handling) { + log_error_write(srv, __FILE__, __LINE__, "s", "-- NOT handling file as static file, extension forbidden"); + } return HANDLER_GO_ON; } } - + if (con->conf.log_request_handling) { log_error_write(srv, __FILE__, __LINE__, "s", "-- handling file as static file"); } - + if (HANDLER_ERROR == stat_cache_get_entry(srv, con, con->physical.path, &sce)) { con->http_status = 403; - + log_error_write(srv, __FILE__, __LINE__, "sbsb", "not a regular file:", con->uri.path, "->", con->physical.path); - + return HANDLER_FINISHED; } - + /* we only handline regular files */ +#ifdef HAVE_LSTAT + if ((sce->is_symlink == 1) && !con->conf.follow_symlink) { + con->http_status = 403; + + if (con->conf.log_request_handling) { + log_error_write(srv, __FILE__, __LINE__, "s", "-- access denied due symlink restriction"); + log_error_write(srv, __FILE__, __LINE__, "sb", "Path :", con->physical.path); + } + + buffer_reset(con->physical.path); + return HANDLER_FINISHED; + } +#endif if (!S_ISREG(sce->st.st_mode)) { con->http_status = 404; - + if (con->conf.log_file_not_found) { log_error_write(srv, __FILE__, __LINE__, "sbsb", "not a regular file:", con->uri.path, "->", sce->name); } - + return HANDLER_FINISHED; } /* mod_compress might set several data directly, don't overwrite them */ - + /* set response content-type, if not set already */ if (NULL == array_get_element(con->response.headers, "Content-Type")) { if (buffer_is_empty(sce->content_type)) { + /* we are setting application/octet-stream, but also announce that + * this header field might change in the seconds few requests + * + * This should fix the aggressive caching of FF and the script download + * seen by the first installations + */ response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_STR_LEN("application/octet-stream")); + + allow_caching = 0; } else { response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_BUF_LEN(sce->content_type)); } } - - if (NULL == array_get_element(con->response.headers, "ETag")) { - /* generate e-tag */ - etag_mutate(con->physical.etag, sce->etag); - - response_header_overwrite(srv, con, CONST_STR_LEN("ETag"), CONST_BUF_LEN(con->physical.etag)); + + if (con->conf.range_requests) { + response_header_overwrite(srv, con, CONST_STR_LEN("Accept-Ranges"), CONST_STR_LEN("bytes")); } - response_header_overwrite(srv, con, CONST_STR_LEN("Accept-Ranges"), CONST_STR_LEN("bytes")); - - /* prepare header */ - if (NULL == (ds = (data_string *)array_get_element(con->response.headers, "Last-Modified"))) { - mtime = strftime_cache_get(srv, sce->st.st_mtime); - response_header_overwrite(srv, con, CONST_STR_LEN("Last-Modified"), CONST_BUF_LEN(mtime)); - } else { - mtime = ds->value; + + if (allow_caching) { + if (p->conf.etags_used && con->etag_flags != 0 && !buffer_is_empty(sce->etag)) { + if (NULL == array_get_element(con->response.headers, "ETag")) { + /* generate e-tag */ + etag_mutate(con->physical.etag, sce->etag); + + response_header_overwrite(srv, con, CONST_STR_LEN("ETag"), CONST_BUF_LEN(con->physical.etag)); + } + } + + /* prepare header */ + if (NULL == (ds = (data_string *)array_get_element(con->response.headers, "Last-Modified"))) { + mtime = strftime_cache_get(srv, sce->st.st_mtime); + response_header_overwrite(srv, con, CONST_STR_LEN("Last-Modified"), CONST_BUF_LEN(mtime)); + } else { + mtime = ds->value; + } + + if (HANDLER_FINISHED == http_response_handle_cachable(srv, con, mtime)) { + return HANDLER_FINISHED; + } } - if (HANDLER_FINISHED == http_response_handle_cachable(srv, con, mtime)) { - return HANDLER_FINISHED; - } else if (con->request.http_range && con->conf.range_requests) { + if (con->request.http_range && con->conf.range_requests) { int do_range_request = 1; /* check if we have a conditional GET */ - if (NULL != (ds = array_get_element(con->request.headers, "If-Range"))) { - /* if the value is the same as our ETag, we do a Range-request, + if (NULL != (ds = (data_string *)array_get_element(con->request.headers, "If-Range"))) { + /* if the value is the same as our ETag, we do a Range-request, * otherwise a full 200 */ - if (!buffer_is_equal(ds->value, con->physical.etag)) { + if (ds->value->ptr[0] == '"') { + /** + * client wants a ETag + */ + if (!con->physical.etag) { + do_range_request = 0; + } else if (!buffer_is_equal(ds->value, con->physical.etag)) { + do_range_request = 0; + } + } else if (!mtime) { + /** + * we don't have a Last-Modified and can match the If-Range: + * + * sending all + */ + do_range_request = 0; + } else if (!buffer_is_equal(ds->value, mtime)) { do_range_request = 0; } } - + if (do_range_request) { /* content prepared, I'm done */ con->file_finished = 1; - + if (0 == http_response_parse_range(srv, con, p)) { con->http_status = 206; } return HANDLER_FINISHED; } } - + /* if we are still here, prepare body */ - - /* we add it here for all requests - * the HEAD request will drop it afterwards again + + /* we add it here for all requests + * the HEAD request will drop it afterwards again */ http_chunk_append_file(srv, con, con->physical.path, 0, sce->st.st_size); - + + con->http_status = 200; con->file_finished = 1; - + return HANDLER_FINISHED; } /* this function is called at dlopen() time and inits the callbacks */ +int mod_staticfile_plugin_init(plugin *p); int mod_staticfile_plugin_init(plugin *p) { p->version = LIGHTTPD_VERSION_ID; p->name = buffer_init_string("staticfile"); - + p->init = mod_staticfile_init; p->handle_subrequest_start = mod_staticfile_subrequest; p->set_defaults = mod_staticfile_set_defaults; p->cleanup = mod_staticfile_free; - + p->data = NULL; - + return 0; } diff --git a/src/mod_status.c b/src/mod_status.c index f5a35e9..76061f9 100644 --- a/src/mod_status.c +++ b/src/mod_status.c @@ -1,4 +1,13 @@ -#define _GNU_SOURCE +#include "server.h" +#include "connections.h" +#include "response.h" +#include "connections.h" +#include "log.h" + +#include "plugin.h" + +#include "inet_ntop_cache.h" + #include <sys/types.h> #include <fcntl.h> @@ -9,15 +18,7 @@ #include <time.h> #include <stdio.h> -#include "server.h" -#include "connections.h" -#include "response.h" -#include "connections.h" -#include "log.h" - -#include "plugin.h" - -#include "inet_ntop_cache.h" +#include "version.h" typedef struct { buffer *config_url; @@ -29,161 +30,161 @@ typedef struct { typedef struct { PLUGIN_DATA; - + double traffic_out; double requests; - + double mod_5s_traffic_out[5]; double mod_5s_requests[5]; size_t mod_5s_ndx; - + double rel_traffic_out; double rel_requests; - + double abs_traffic_out; double abs_requests; - + double bytes_written; - + buffer *module_list; - + plugin_config **config_storage; - - plugin_config conf; + + plugin_config conf; } plugin_data; INIT_FUNC(mod_status_init) { plugin_data *p; size_t i; - + p = calloc(1, sizeof(*p)); - + p->traffic_out = p->requests = 0; p->rel_traffic_out = p->rel_requests = 0; p->abs_traffic_out = p->abs_requests = 0; p->bytes_written = 0; p->module_list = buffer_init(); - + for (i = 0; i < 5; i++) { p->mod_5s_traffic_out[i] = p->mod_5s_requests[i] = 0; } - + return p; } FREE_FUNC(mod_status_free) { plugin_data *p = p_d; - + UNUSED(srv); if (!p) return HANDLER_GO_ON; - + buffer_free(p->module_list); - + if (p->config_storage) { size_t i; for (i = 0; i < srv->config_context->used; i++) { plugin_config *s = p->config_storage[i]; - + buffer_free(s->status_url); buffer_free(s->statistics_url); buffer_free(s->config_url); - + free(s); } free(p->config_storage); } - - + + free(p); - + return HANDLER_GO_ON; } SETDEFAULTS_FUNC(mod_status_set_defaults) { plugin_data *p = p_d; size_t i; - - config_values_t cv[] = { + + config_values_t cv[] = { { "status.status-url", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, { "status.config-url", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, { "status.enable-sort", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, { "status.statistics-url", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET } }; - + if (!p) return HANDLER_ERROR; - + p->config_storage = calloc(1, srv->config_context->used * sizeof(specific_config *)); - + for (i = 0; i < srv->config_context->used; i++) { plugin_config *s; - + s = calloc(1, sizeof(plugin_config)); s->config_url = buffer_init(); s->status_url = buffer_init(); s->sort = 1; s->statistics_url = buffer_init(); - + cv[0].destination = s->status_url; cv[1].destination = s->config_url; cv[2].destination = &(s->sort); cv[3].destination = s->statistics_url; - + p->config_storage[i] = s; - + if (0 != config_insert_values_global(srv, ((data_config *)srv->config_context->data[i])->value, cv)) { return HANDLER_ERROR; } } - + return HANDLER_GO_ON; } static int mod_status_row_append(buffer *b, const char *key, const char *value) { - BUFFER_APPEND_STRING_CONST(b, " <tr>\n"); - BUFFER_APPEND_STRING_CONST(b, " <td><b>"); + buffer_append_string_len(b, CONST_STR_LEN(" <tr>\n")); + buffer_append_string_len(b, CONST_STR_LEN(" <td><b>")); buffer_append_string(b, key); - BUFFER_APPEND_STRING_CONST(b, "</b></td>\n"); - BUFFER_APPEND_STRING_CONST(b, " <td>"); + buffer_append_string_len(b, CONST_STR_LEN("</b></td>\n")); + buffer_append_string_len(b, CONST_STR_LEN(" <td>")); buffer_append_string(b, value); - BUFFER_APPEND_STRING_CONST(b, "</td>\n"); - BUFFER_APPEND_STRING_CONST(b, " </tr>\n"); - + buffer_append_string_len(b, CONST_STR_LEN("</td>\n")); + buffer_append_string_len(b, CONST_STR_LEN(" </tr>\n")); + return 0; } static int mod_status_header_append(buffer *b, const char *key) { - BUFFER_APPEND_STRING_CONST(b, " <tr>\n"); - BUFFER_APPEND_STRING_CONST(b, " <th colspan=\"2\">"); + buffer_append_string_len(b, CONST_STR_LEN(" <tr>\n")); + buffer_append_string_len(b, CONST_STR_LEN(" <th colspan=\"2\">")); buffer_append_string(b, key); - BUFFER_APPEND_STRING_CONST(b, "</th>\n"); - BUFFER_APPEND_STRING_CONST(b, " </tr>\n"); - + buffer_append_string_len(b, CONST_STR_LEN("</th>\n")); + buffer_append_string_len(b, CONST_STR_LEN(" </tr>\n")); + return 0; } static int mod_status_header_append_sort(buffer *b, void *p_d, const char* key) { plugin_data *p = p_d; - + if (p->conf.sort) { - BUFFER_APPEND_STRING_CONST(b, "<th class=\"status\"><a href=\"#\" class=\"sortheader\" onclick=\"resort(this);return false;\">"); + buffer_append_string_len(b, CONST_STR_LEN("<th class=\"status\"><a href=\"#\" class=\"sortheader\" onclick=\"resort(this);return false;\">")); buffer_append_string(b, key); - BUFFER_APPEND_STRING_CONST(b, "<span class=\"sortarrow\"></span></a></th>\n"); + buffer_append_string_len(b, CONST_STR_LEN("<span class=\"sortarrow\">:</span></a></th>\n")); } else { - BUFFER_APPEND_STRING_CONST(b, "<th class=\"status\">"); + buffer_append_string_len(b, CONST_STR_LEN("<th class=\"status\">")); buffer_append_string(b, key); - BUFFER_APPEND_STRING_CONST(b, "</th>\n"); + buffer_append_string_len(b, CONST_STR_LEN("</th>\n")); } - + return 0; } static int mod_status_get_multiplier(double *avg, char *multiplier, int size) { *multiplier = ' '; - + if (*avg > size) { *avg /= size; *multiplier = 'k'; } if (*avg > size) { *avg /= size; *multiplier = 'M'; } if (*avg > size) { *avg /= size; *multiplier = 'G'; } @@ -204,37 +205,36 @@ static handler_t mod_status_handle_server_status_html(server *srv, connection *c char multiplier = '\0'; char buf[32]; time_t ts; - + int days, hours, mins, seconds; - + b = chunkqueue_get_append_buffer(con->write_queue); - BUFFER_COPY_STRING_CONST(b, + buffer_copy_string_len(b, CONST_STR_LEN( "<?xml version=\"1.0\" encoding=\"iso-8859-1\"?>\n" "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\"\n" " \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n" "<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" lang=\"en\">\n" " <head>\n" - " <title>Status</title>\n"); - - BUFFER_APPEND_STRING_CONST(b, + " <title>Status</title>\n" + " <style type=\"text/css\">\n" " table.status { border: black solid thin; }\n" + " td { white-space: nowrap; }\n" " td.int { background-color: #f0f0f0; text-align: right }\n" " td.string { background-color: #f0f0f0; text-align: left }\n" " th.status { background-color: black; color: white; font-weight: bold; }\n" " a.sortheader { background-color: black; color: white; font-weight: bold; text-decoration: none; display: block; }\n" " span.sortarrow { color: white; text-decoration: none; }\n" - " </style>\n"); - + " </style>\n")); + if (p->conf.sort) { - BUFFER_APPEND_STRING_CONST(b, + buffer_append_string_len(b, CONST_STR_LEN( "<script type=\"text/javascript\">\n" "// <!--\n" "var sort_column;\n" - "var prev_span = null;\n"); - - BUFFER_APPEND_STRING_CONST(b, + "var prev_span = null;\n" + "function get_inner_text(el) {\n" " if((typeof el == 'string')||(typeof el == 'undefined'))\n" " return el;\n" @@ -250,9 +250,8 @@ static handler_t mod_status_handle_server_status_html(server *srv, connection *c " }\n" " }\n" " return str;\n" - "}\n"); - - BUFFER_APPEND_STRING_CONST(b, + "}\n" + "function sortfn(a,b) {\n" " var at = get_inner_text(a.cells[sort_column]);\n" " var bt = get_inner_text(b.cells[sort_column]);\n" @@ -265,9 +264,8 @@ static handler_t mod_status_handle_server_status_html(server *srv, connection *c " else if (aa<bb) return -1;\n" " else return 1;\n" " }\n" - "}\n"); - - BUFFER_APPEND_STRING_CONST(b, + "}\n" + "function resort(lnk) {\n" " var span = lnk.childNodes[1];\n" " var table = lnk.parentNode.parentNode.parentNode.parentNode;\n" @@ -275,9 +273,8 @@ static handler_t mod_status_handle_server_status_html(server *srv, connection *c " for (j=1;j<table.rows.length;j++)\n" " rows[j-1] = table.rows[j];\n" " sort_column = lnk.parentNode.cellIndex;\n" - " rows.sort(sortfn);\n"); - - BUFFER_APPEND_STRING_CONST(b, + " rows.sort(sortfn);\n" + " if (prev_span != null) prev_span.innerHTML = '';\n" " if (span.getAttribute('sortdir')=='down') {\n" " span.innerHTML = '↑';\n" @@ -292,178 +289,185 @@ static handler_t mod_status_handle_server_status_html(server *srv, connection *c " prev_span = span;\n" "}\n" "// -->\n" - "</script>\n"); + "</script>\n")); } - - BUFFER_APPEND_STRING_CONST(b, + + buffer_append_string_len(b, CONST_STR_LEN( " </head>\n" - " <body>\n"); - - - + " <body>\n")); + + + /* connection listing */ - BUFFER_APPEND_STRING_CONST(b, "<h1>Server-Status</h1>"); - - BUFFER_APPEND_STRING_CONST(b, "<table class=\"status\">"); - BUFFER_APPEND_STRING_CONST(b, "<tr><td>Hostname</td><td class=\"string\">"); + buffer_append_string_len(b, CONST_STR_LEN("<h1>Server-Status (" PACKAGE_NAME " " PACKAGE_VERSION ")</h1>")); + + buffer_append_string_len(b, CONST_STR_LEN("<table summary=\"status\" class=\"status\">")); + buffer_append_string_len(b, CONST_STR_LEN("<tr><td>Hostname</td><td class=\"string\">")); buffer_append_string_buffer(b, con->uri.authority); - BUFFER_APPEND_STRING_CONST(b, " ("); + buffer_append_string_len(b, CONST_STR_LEN(" (")); buffer_append_string_buffer(b, con->server_name); - BUFFER_APPEND_STRING_CONST(b, ")</td></tr>\n"); - BUFFER_APPEND_STRING_CONST(b, "<tr><td>Uptime</td><td class=\"string\">"); - + buffer_append_string_len(b, CONST_STR_LEN(")</td></tr>\n")); + buffer_append_string_len(b, CONST_STR_LEN("<tr><td>Uptime</td><td class=\"string\">")); + ts = srv->cur_ts - srv->startup_ts; - + days = ts / (60 * 60 * 24); ts %= (60 * 60 * 24); - + hours = ts / (60 * 60); ts %= (60 * 60); - + mins = ts / (60); ts %= (60); - + seconds = ts; - + if (days) { buffer_append_long(b, days); - BUFFER_APPEND_STRING_CONST(b, " days "); + buffer_append_string_len(b, CONST_STR_LEN(" days ")); } - + if (hours) { buffer_append_long(b, hours); - BUFFER_APPEND_STRING_CONST(b, " hours "); + buffer_append_string_len(b, CONST_STR_LEN(" hours ")); } - + if (mins) { buffer_append_long(b, mins); - BUFFER_APPEND_STRING_CONST(b, " min "); + buffer_append_string_len(b, CONST_STR_LEN(" min ")); } - + buffer_append_long(b, seconds); - BUFFER_APPEND_STRING_CONST(b, " s"); - - BUFFER_APPEND_STRING_CONST(b, "</td></tr>\n"); - BUFFER_APPEND_STRING_CONST(b, "<tr><td>Started at</td><td class=\"string\">"); - + buffer_append_string_len(b, CONST_STR_LEN(" s")); + + buffer_append_string_len(b, CONST_STR_LEN("</td></tr>\n")); + buffer_append_string_len(b, CONST_STR_LEN("<tr><td>Started at</td><td class=\"string\">")); + ts = srv->startup_ts; - + strftime(buf, sizeof(buf) - 1, "%Y-%m-%d %H:%M:%S", localtime(&ts)); buffer_append_string(b, buf); - BUFFER_APPEND_STRING_CONST(b, "</td></tr>\n"); - - - BUFFER_APPEND_STRING_CONST(b, "<tr><th colspan=\"2\">absolute (since start)</th></tr>\n"); - - BUFFER_APPEND_STRING_CONST(b, "<tr><td>Requests</td><td class=\"string\">"); + buffer_append_string_len(b, CONST_STR_LEN("</td></tr>\n")); + + + buffer_append_string_len(b, CONST_STR_LEN("<tr><th colspan=\"2\">absolute (since start)</th></tr>\n")); + + buffer_append_string_len(b, CONST_STR_LEN("<tr><td>Requests</td><td class=\"string\">")); avg = p->abs_requests; mod_status_get_multiplier(&avg, &multiplier, 1000); - + buffer_append_long(b, avg); - BUFFER_APPEND_STRING_CONST(b, " "); + buffer_append_string_len(b, CONST_STR_LEN(" ")); if (multiplier) buffer_append_string_len(b, &multiplier, 1); - BUFFER_APPEND_STRING_CONST(b, "req</td></tr>\n"); - - BUFFER_APPEND_STRING_CONST(b, "<tr><td>Traffic</td><td class=\"string\">"); + buffer_append_string_len(b, CONST_STR_LEN("req</td></tr>\n")); + + buffer_append_string_len(b, CONST_STR_LEN("<tr><td>Traffic</td><td class=\"string\">")); avg = p->abs_traffic_out; mod_status_get_multiplier(&avg, &multiplier, 1024); sprintf(buf, "%.2f", avg); buffer_append_string(b, buf); - BUFFER_APPEND_STRING_CONST(b, " "); + buffer_append_string_len(b, CONST_STR_LEN(" ")); if (multiplier) buffer_append_string_len(b, &multiplier, 1); - BUFFER_APPEND_STRING_CONST(b, "byte</td></tr>\n"); + buffer_append_string_len(b, CONST_STR_LEN("byte</td></tr>\n")); + + buffer_append_string_len(b, CONST_STR_LEN("<tr><th colspan=\"2\">average (since start)</th></tr>\n")); - BUFFER_APPEND_STRING_CONST(b, "<tr><th colspan=\"2\">average (since start)</th></tr>\n"); - - BUFFER_APPEND_STRING_CONST(b, "<tr><td>Requests</td><td class=\"string\">"); + buffer_append_string_len(b, CONST_STR_LEN("<tr><td>Requests</td><td class=\"string\">")); avg = p->abs_requests / (srv->cur_ts - srv->startup_ts); mod_status_get_multiplier(&avg, &multiplier, 1000); buffer_append_long(b, avg); - BUFFER_APPEND_STRING_CONST(b, " "); + buffer_append_string_len(b, CONST_STR_LEN(" ")); if (multiplier) buffer_append_string_len(b, &multiplier, 1); - BUFFER_APPEND_STRING_CONST(b, "req/s</td></tr>\n"); - - BUFFER_APPEND_STRING_CONST(b, "<tr><td>Traffic</td><td class=\"string\">"); + buffer_append_string_len(b, CONST_STR_LEN("req/s</td></tr>\n")); + + buffer_append_string_len(b, CONST_STR_LEN("<tr><td>Traffic</td><td class=\"string\">")); avg = p->abs_traffic_out / (srv->cur_ts - srv->startup_ts); mod_status_get_multiplier(&avg, &multiplier, 1024); sprintf(buf, "%.2f", avg); buffer_append_string(b, buf); - BUFFER_APPEND_STRING_CONST(b, " "); + buffer_append_string_len(b, CONST_STR_LEN(" ")); if (multiplier) buffer_append_string_len(b, &multiplier, 1); - BUFFER_APPEND_STRING_CONST(b, "byte/s</td></tr>\n"); + buffer_append_string_len(b, CONST_STR_LEN("byte/s</td></tr>\n")); - - - BUFFER_APPEND_STRING_CONST(b, "<tr><th colspan=\"2\">average (5s sliding average)</th></tr>\n"); + + + buffer_append_string_len(b, CONST_STR_LEN("<tr><th colspan=\"2\">average (5s sliding average)</th></tr>\n")); for (j = 0, avg = 0; j < 5; j++) { avg += p->mod_5s_requests[j]; } - + avg /= 5; - - BUFFER_APPEND_STRING_CONST(b, "<tr><td>Requests</td><td class=\"string\">"); + + buffer_append_string_len(b, CONST_STR_LEN("<tr><td>Requests</td><td class=\"string\">")); mod_status_get_multiplier(&avg, &multiplier, 1000); buffer_append_long(b, avg); - BUFFER_APPEND_STRING_CONST(b, " "); + buffer_append_string_len(b, CONST_STR_LEN(" ")); if (multiplier) buffer_append_string_len(b, &multiplier, 1); - - BUFFER_APPEND_STRING_CONST(b, "req/s</td></tr>\n"); - + + buffer_append_string_len(b, CONST_STR_LEN("req/s</td></tr>\n")); + for (j = 0, avg = 0; j < 5; j++) { avg += p->mod_5s_traffic_out[j]; } - + avg /= 5; - - BUFFER_APPEND_STRING_CONST(b, "<tr><td>Traffic</td><td class=\"string\">"); + + buffer_append_string_len(b, CONST_STR_LEN("<tr><td>Traffic</td><td class=\"string\">")); mod_status_get_multiplier(&avg, &multiplier, 1024); sprintf(buf, "%.2f", avg); buffer_append_string(b, buf); - BUFFER_APPEND_STRING_CONST(b, " "); + buffer_append_string_len(b, CONST_STR_LEN(" ")); if (multiplier) buffer_append_string_len(b, &multiplier, 1); - BUFFER_APPEND_STRING_CONST(b, "byte/s</td></tr>\n"); - - BUFFER_APPEND_STRING_CONST(b, "</table>\n"); - - - BUFFER_APPEND_STRING_CONST(b, "<hr />\n<pre><b>legend</b>\n"); - BUFFER_APPEND_STRING_CONST(b, ". = connect, C = close, E = hard error\n"); - BUFFER_APPEND_STRING_CONST(b, "r = read, R = read-POST, W = write, h = handle-request\n"); - BUFFER_APPEND_STRING_CONST(b, "q = request-start, Q = request-end\n"); - BUFFER_APPEND_STRING_CONST(b, "s = response-start, S = response-end\n"); - - BUFFER_APPEND_STRING_CONST(b, "<b>"); + buffer_append_string_len(b, CONST_STR_LEN("byte/s</td></tr>\n")); + + buffer_append_string_len(b, CONST_STR_LEN("</table>\n")); + + + buffer_append_string_len(b, CONST_STR_LEN( + "<hr />\n<pre><b>legend</b>\n" + ". = connect, C = close, E = hard error, k = keep-alive\n" + "r = read, R = read-POST, W = write, h = handle-request\n" + "q = request-start, Q = request-end\n" + "s = response-start, S = response-end\n")); + + buffer_append_string_len(b, CONST_STR_LEN("<b>")); buffer_append_long(b, srv->conns->used); - BUFFER_APPEND_STRING_CONST(b, " connections</b>\n"); - + buffer_append_string_len(b, CONST_STR_LEN(" connections</b>\n")); + for (j = 0; j < srv->conns->used; j++) { connection *c = srv->conns->ptr[j]; - const char *state = connection_get_short_state(c->state); - + const char *state; + + if (CON_STATE_READ == c->state && c->request.orig_uri->used > 0) { + state = "k"; + } else { + state = connection_get_short_state(c->state); + } + buffer_append_string_len(b, state, 1); - + if (((j + 1) % 50) == 0) { - BUFFER_APPEND_STRING_CONST(b, "\n"); + buffer_append_string_len(b, CONST_STR_LEN("\n")); } } - - BUFFER_APPEND_STRING_CONST(b, "\n</pre><hr />\n<h2>Connections</h2>\n"); - - BUFFER_APPEND_STRING_CONST(b, "<table class=\"status\">\n"); - BUFFER_APPEND_STRING_CONST(b, "<tr>"); + + buffer_append_string_len(b, CONST_STR_LEN("\n</pre><hr />\n<h2>Connections</h2>\n")); + + buffer_append_string_len(b, CONST_STR_LEN("<table summary=\"status\" class=\"status\">\n")); + buffer_append_string_len(b, CONST_STR_LEN("<tr>")); mod_status_header_append_sort(b, p_d, "Client IP"); mod_status_header_append_sort(b, p_d, "Read"); mod_status_header_append_sort(b, p_d, "Written"); @@ -472,73 +476,87 @@ static handler_t mod_status_handle_server_status_html(server *srv, connection *c mod_status_header_append_sort(b, p_d, "Host"); mod_status_header_append_sort(b, p_d, "URI"); mod_status_header_append_sort(b, p_d, "File"); - BUFFER_APPEND_STRING_CONST(b, "</tr>\n"); - + buffer_append_string_len(b, CONST_STR_LEN("</tr>\n")); + for (j = 0; j < srv->conns->used; j++) { connection *c = srv->conns->ptr[j]; - - BUFFER_APPEND_STRING_CONST(b, "<tr><td class=\"string\">"); - + + buffer_append_string_len(b, CONST_STR_LEN("<tr><td class=\"string\">")); + buffer_append_string(b, inet_ntop_cache_get_ip(srv, &(c->dst_addr))); - - BUFFER_APPEND_STRING_CONST(b, "</td><td class=\"int\">"); - - if (con->request.content_length) { + + buffer_append_string_len(b, CONST_STR_LEN("</td><td class=\"int\">")); + + if (c->request.content_length) { buffer_append_long(b, c->request_content_queue->bytes_in); - BUFFER_APPEND_STRING_CONST(b, "/"); + buffer_append_string_len(b, CONST_STR_LEN("/")); buffer_append_long(b, c->request.content_length); } else { - BUFFER_APPEND_STRING_CONST(b, "0/0"); + buffer_append_string_len(b, CONST_STR_LEN("0/0")); } - - BUFFER_APPEND_STRING_CONST(b, "</td><td class=\"int\">"); - + + buffer_append_string_len(b, CONST_STR_LEN("</td><td class=\"int\">")); + buffer_append_off_t(b, chunkqueue_written(c->write_queue)); - BUFFER_APPEND_STRING_CONST(b, "/"); + buffer_append_string_len(b, CONST_STR_LEN("/")); buffer_append_off_t(b, chunkqueue_length(c->write_queue)); - - BUFFER_APPEND_STRING_CONST(b, "</td><td class=\"string\">"); - - buffer_append_string(b, connection_get_state(c->state)); - - BUFFER_APPEND_STRING_CONST(b, "</td><td class=\"int\">"); - + + buffer_append_string_len(b, CONST_STR_LEN("</td><td class=\"string\">")); + + if (CON_STATE_READ == c->state && c->request.orig_uri->used > 0) { + buffer_append_string_len(b, CONST_STR_LEN("keep-alive")); + } else { + buffer_append_string(b, connection_get_state(c->state)); + } + + buffer_append_string_len(b, CONST_STR_LEN("</td><td class=\"int\">")); + buffer_append_long(b, srv->cur_ts - c->request_start); - - BUFFER_APPEND_STRING_CONST(b, "</td><td class=\"string\">"); - + + buffer_append_string_len(b, CONST_STR_LEN("</td><td class=\"string\">")); + if (buffer_is_empty(c->server_name)) { buffer_append_string_buffer(b, c->uri.authority); } else { buffer_append_string_buffer(b, c->server_name); } - - BUFFER_APPEND_STRING_CONST(b, "</td><td class=\"string\">"); - + + buffer_append_string_len(b, CONST_STR_LEN("</td><td class=\"string\">")); + if (!buffer_is_empty(c->uri.path)) { buffer_append_string_encoded(b, CONST_BUF_LEN(c->uri.path), ENCODING_HTML); } - - BUFFER_APPEND_STRING_CONST(b, "</td><td class=\"string\">"); - + + if (!buffer_is_empty(c->uri.query)) { + buffer_append_string_len(b, CONST_STR_LEN("?")); + buffer_append_string_encoded(b, CONST_BUF_LEN(c->uri.query), ENCODING_HTML); + } + + if (!buffer_is_empty(c->request.orig_uri)) { + buffer_append_string_len(b, CONST_STR_LEN(" (")); + buffer_append_string_encoded(b, CONST_BUF_LEN(c->request.orig_uri), ENCODING_HTML); + buffer_append_string_len(b, CONST_STR_LEN(")")); + } + buffer_append_string_len(b, CONST_STR_LEN("</td><td class=\"string\">")); + buffer_append_string_buffer(b, c->physical.path); - - BUFFER_APPEND_STRING_CONST(b, "</td></tr>\n"); + + buffer_append_string_len(b, CONST_STR_LEN("</td></tr>\n")); } - - - BUFFER_APPEND_STRING_CONST(b, - "</table>\n"); - - - BUFFER_APPEND_STRING_CONST(b, + + + buffer_append_string_len(b, CONST_STR_LEN( + "</table>\n")); + + + buffer_append_string_len(b, CONST_STR_LEN( " </body>\n" "</html>\n" - ); - + )); + response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_STR_LEN("text/html")); - + return 0; } @@ -548,84 +566,105 @@ static handler_t mod_status_handle_server_status_text(server *srv, connection *c buffer *b; double avg; time_t ts; - + char buf[32]; + unsigned int k; + unsigned int l; + b = chunkqueue_get_append_buffer(con->write_queue); /* output total number of requests */ - BUFFER_APPEND_STRING_CONST(b, "Total Accesses: "); + buffer_append_string_len(b, CONST_STR_LEN("Total Accesses: ")); avg = p->abs_requests; - buffer_append_long(b, avg); - BUFFER_APPEND_STRING_CONST(b, "\n"); - + snprintf(buf, sizeof(buf) - 1, "%.0f", avg); + buffer_append_string(b, buf); + buffer_append_string_len(b, CONST_STR_LEN("\n")); + /* output total traffic out in kbytes */ - BUFFER_APPEND_STRING_CONST(b, "Total kBytes: "); + buffer_append_string_len(b, CONST_STR_LEN("Total kBytes: ")); avg = p->abs_traffic_out / 1024; - buffer_append_long(b, avg); - BUFFER_APPEND_STRING_CONST(b, "\n"); - + snprintf(buf, sizeof(buf) - 1, "%.0f", avg); + buffer_append_string(b, buf); + buffer_append_string_len(b, CONST_STR_LEN("\n")); + /* output uptime */ - BUFFER_APPEND_STRING_CONST(b, "Uptime: "); + buffer_append_string_len(b, CONST_STR_LEN("Uptime: ")); ts = srv->cur_ts - srv->startup_ts; buffer_append_long(b, ts); - BUFFER_APPEND_STRING_CONST(b, "\n"); - + buffer_append_string_len(b, CONST_STR_LEN("\n")); + /* output busy servers */ - BUFFER_APPEND_STRING_CONST(b, "BusyServers: "); + buffer_append_string_len(b, CONST_STR_LEN("BusyServers: ")); buffer_append_long(b, srv->conns->used); - BUFFER_APPEND_STRING_CONST(b, "\n"); + buffer_append_string_len(b, CONST_STR_LEN("\n")); + + buffer_append_string_len(b, CONST_STR_LEN("IdleServers: ")); + buffer_append_long(b, srv->conns->size - srv->conns->used); + buffer_append_string_len(b, CONST_STR_LEN("\n")); + + /* output scoreboard */ + buffer_append_string_len(b, CONST_STR_LEN("Scoreboard: ")); + for (k = 0; k < srv->conns->used; k++) { + connection *c = srv->conns->ptr[k]; + const char *state = connection_get_short_state(c->state); + buffer_append_string_len(b, state, 1); + } + for (l = 0; l < srv->conns->size - srv->conns->used; l++) { + buffer_append_string_len(b, CONST_STR_LEN("_")); + } + buffer_append_string_len(b, CONST_STR_LEN("\n")); /* set text/plain output */ response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_STR_LEN("text/plain")); - + return 0; } static handler_t mod_status_handle_server_statistics(server *srv, connection *con, void *p_d) { - plugin_data *p = p_d; - buffer *b, *m = p->module_list; + buffer *b; size_t i; array *st = srv->status; + UNUSED(p_d); if (0 == st->used) { /* we have nothing to send */ con->http_status = 204; con->file_finished = 1; - + return HANDLER_FINISHED; } - + b = chunkqueue_get_append_buffer(con->write_queue); for (i = 0; i < st->used; i++) { size_t ndx = st->sorted[i]; buffer_append_string_buffer(b, st->data[ndx]->key); - buffer_append_string(b, ": "); + buffer_append_string_len(b, CONST_STR_LEN(": ")); buffer_append_long(b, ((data_integer *)(st->data[ndx]))->value); - buffer_append_string(b, "\n"); + buffer_append_string_len(b, CONST_STR_LEN("\n")); } - + response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_STR_LEN("text/plain")); - + con->http_status = 200; con->file_finished = 1; - + return HANDLER_FINISHED; } static handler_t mod_status_handle_server_status(server *srv, connection *con, void *p_d) { - + if (buffer_is_equal_string(con->uri.query, CONST_STR_LEN("auto"))) { mod_status_handle_server_status_text(srv, con, p_d); } else { mod_status_handle_server_status_html(srv, con, p_d); } - + con->http_status = 200; con->file_finished = 1; - + return HANDLER_FINISHED; } @@ -634,37 +673,39 @@ static handler_t mod_status_handle_server_config(server *srv, connection *con, v plugin_data *p = p_d; buffer *b, *m = p->module_list; size_t i; - - struct ev_map { fdevent_handler_t et; const char *name; } event_handlers[] = - { - /* - poll is most reliable + + struct ev_map { fdevent_handler_t et; const char *name; } event_handlers[] = + { + /* - epoll is most reliable * - select works everywhere - * - linux-* are experimental */ +#ifdef USE_LINUX_EPOLL + { FDEVENT_HANDLER_LINUX_SYSEPOLL, "linux-sysepoll" }, +#endif #ifdef USE_POLL { FDEVENT_HANDLER_POLL, "poll" }, #endif #ifdef USE_SELECT { FDEVENT_HANDLER_SELECT, "select" }, #endif -#ifdef USE_LINUX_EPOLL - { FDEVENT_HANDLER_LINUX_SYSEPOLL, "linux-sysepoll" }, -#endif -#ifdef USE_LINUX_SIGIO - { FDEVENT_HANDLER_LINUX_RTSIG, "linux-rtsig" }, +#ifdef USE_LIBEV + { FDEVENT_HANDLER_LIBEV, "libev" }, #endif #ifdef USE_SOLARIS_DEVPOLL { FDEVENT_HANDLER_SOLARIS_DEVPOLL,"solaris-devpoll" }, #endif +#ifdef USE_SOLARIS_PORT + { FDEVENT_HANDLER_SOLARIS_PORT, "solaris-eventports" }, +#endif #ifdef USE_FREEBSD_KQUEUE { FDEVENT_HANDLER_FREEBSD_KQUEUE, "freebsd-kqueue" }, #endif { FDEVENT_HANDLER_UNSET, NULL } }; - + b = chunkqueue_get_append_buffer(con->write_queue); - - BUFFER_COPY_STRING_CONST(b, + + buffer_copy_string_len(b, CONST_STR_LEN( "<?xml version=\"1.0\" encoding=\"iso-8859-1\"?>\n" "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\"\n" " \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n" @@ -673,9 +714,9 @@ static handler_t mod_status_handle_server_config(server *srv, connection *con, v " <title>Status</title>\n" " </head>\n" " <body>\n" - " <h1>" PACKAGE_NAME " " PACKAGE_VERSION "</h1>\n" - " <table border=\"1\">\n"); - + " <h1>" PACKAGE_DESC "</h1>\n" + " <table summary=\"status\" border=\"1\">\n")); + mod_status_header_append(b, "Server-Features"); #ifdef HAVE_PCRE_H mod_status_row_append(b, "RegEx Conditionals", "enabled"); @@ -683,43 +724,43 @@ static handler_t mod_status_handle_server_config(server *srv, connection *con, v mod_status_row_append(b, "RegEx Conditionals", "disabled - pcre missing"); #endif mod_status_header_append(b, "Network Engine"); - + for (i = 0; event_handlers[i].name; i++) { if (event_handlers[i].et == srv->event_handler) { mod_status_row_append(b, "fd-Event-Handler", event_handlers[i].name); break; } } - + mod_status_header_append(b, "Config-File-Settings"); - + for (i = 0; i < srv->plugins.used; i++) { plugin **ps = srv->plugins.ptr; - + plugin *pl = ps[i]; - + if (i == 0) { buffer_copy_string_buffer(m, pl->name); } else { - BUFFER_APPEND_STRING_CONST(m, "<br />"); + buffer_append_string_len(m, CONST_STR_LEN("<br />")); buffer_append_string_buffer(m, pl->name); } } - + mod_status_row_append(b, "Loaded Modules", m->ptr); - - BUFFER_APPEND_STRING_CONST(b, " </table>\n"); - - BUFFER_APPEND_STRING_CONST(b, + + buffer_append_string_len(b, CONST_STR_LEN(" </table>\n")); + + buffer_append_string_len(b, CONST_STR_LEN( " </body>\n" "</html>\n" - ); - + )); + response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_STR_LEN("text/html")); - + con->http_status = 200; con->file_finished = 1; - + return HANDLER_FINISHED; } @@ -728,24 +769,24 @@ static handler_t mod_status_handle_server_config(server *srv, connection *con, v static int mod_status_patch_connection(server *srv, connection *con, plugin_data *p) { size_t i, j; plugin_config *s = p->config_storage[0]; - + PATCH(status_url); PATCH(config_url); PATCH(sort); PATCH(statistics_url); - + /* skip the first, the global context */ for (i = 1; i < srv->config_context->used; i++) { data_config *dc = (data_config *)srv->config_context->data[i]; s = p->config_storage[i]; - + /* condition didn't match */ if (!config_check_cond(srv, con, dc)) continue; - + /* merge config */ for (j = 0; j < dc->value->used; j++) { data_unset *du = dc->value->data[j]; - + if (buffer_is_equal_string(du->key, CONST_STR_LEN("status.status-url"))) { PATCH(status_url); } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("status.config-url"))) { @@ -754,88 +795,91 @@ static int mod_status_patch_connection(server *srv, connection *con, plugin_data PATCH(sort); } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("status.statistics-url"))) { PATCH(statistics_url); - } + } } } - + return 0; } static handler_t mod_status_handler(server *srv, connection *con, void *p_d) { plugin_data *p = p_d; - + + if (con->mode != DIRECT) return HANDLER_GO_ON; + mod_status_patch_connection(srv, con, p); - - if (!buffer_is_empty(p->conf.status_url) && + + if (!buffer_is_empty(p->conf.status_url) && buffer_is_equal(p->conf.status_url, con->uri.path)) { return mod_status_handle_server_status(srv, con, p_d); - } else if (!buffer_is_empty(p->conf.config_url) && + } else if (!buffer_is_empty(p->conf.config_url) && buffer_is_equal(p->conf.config_url, con->uri.path)) { return mod_status_handle_server_config(srv, con, p_d); - } else if (!buffer_is_empty(p->conf.statistics_url) && + } else if (!buffer_is_empty(p->conf.statistics_url) && buffer_is_equal(p->conf.statistics_url, con->uri.path)) { return mod_status_handle_server_statistics(srv, con, p_d); } - + return HANDLER_GO_ON; } TRIGGER_FUNC(mod_status_trigger) { plugin_data *p = p_d; size_t i; - + /* check all connections */ for (i = 0; i < srv->conns->used; i++) { connection *c = srv->conns->ptr[i]; - + p->bytes_written += c->bytes_written_cur_second; } - + /* a sliding average */ p->mod_5s_traffic_out[p->mod_5s_ndx] = p->bytes_written; p->mod_5s_requests [p->mod_5s_ndx] = p->requests; - + p->mod_5s_ndx = (p->mod_5s_ndx+1) % 5; - + p->abs_traffic_out += p->bytes_written; p->rel_traffic_out += p->bytes_written; - + p->bytes_written = 0; - + /* reset storage - second */ p->traffic_out = 0; p->requests = 0; - + return HANDLER_GO_ON; } REQUESTDONE_FUNC(mod_status_account) { plugin_data *p = p_d; - + UNUSED(srv); p->requests++; p->rel_requests++; p->abs_requests++; - + p->bytes_written += con->bytes_written_cur_second; - + return HANDLER_GO_ON; } +int mod_status_plugin_init(plugin *p); int mod_status_plugin_init(plugin *p) { p->version = LIGHTTPD_VERSION_ID; p->name = buffer_init_string("status"); - + p->init = mod_status_init; p->cleanup = mod_status_free; p->set_defaults= mod_status_set_defaults; - + p->handle_uri_clean = mod_status_handler; p->handle_trigger = mod_status_trigger; p->handle_request_done = mod_status_account; - + p->data = NULL; - + return 0; } diff --git a/src/mod_trigger_b4_dl.c b/src/mod_trigger_b4_dl.c index 8281ec0..f813413 100644 --- a/src/mod_trigger_b4_dl.c +++ b/src/mod_trigger_b4_dl.c @@ -1,7 +1,3 @@ -#include <ctype.h> -#include <stdlib.h> -#include <string.h> - #include "base.h" #include "log.h" #include "buffer.h" @@ -10,32 +6,37 @@ #include "response.h" #include "inet_ntop_cache.h" +#include <ctype.h> +#include <stdlib.h> +#include <fcntl.h> +#include <string.h> + #if defined(HAVE_GDBM_H) -#include <gdbm.h> +# include <gdbm.h> #endif #if defined(HAVE_PCRE_H) -#include <pcre.h> +# include <pcre.h> #endif #if defined(HAVE_MEMCACHE_H) -#include <memcache.h> +# include <memcache.h> #endif /** * this is a trigger_b4_dl for a lighttpd plugin - * + * */ /* plugin config for all request/connections */ typedef struct { buffer *db_filename; - + buffer *trigger_url; buffer *download_url; buffer *deny_url; - + array *mc_hosts; buffer *mc_namespace; #if defined(HAVE_PCRE_H) @@ -46,58 +47,58 @@ typedef struct { GDBM_FILE db; #endif -#if defined(HAVE_MEMCACHE_H) +#if defined(HAVE_MEMCACHE_H) struct memcache *mc; #endif - + unsigned short trigger_timeout; unsigned short debug; } plugin_config; typedef struct { PLUGIN_DATA; - + buffer *tmp_buf; - + plugin_config **config_storage; - - plugin_config conf; + + plugin_config conf; } plugin_data; /* init the plugin data */ INIT_FUNC(mod_trigger_b4_dl_init) { plugin_data *p; - + p = calloc(1, sizeof(*p)); - + p->tmp_buf = buffer_init(); - + return p; } /* detroy the plugin data */ FREE_FUNC(mod_trigger_b4_dl_free) { plugin_data *p = p_d; - + UNUSED(srv); if (!p) return HANDLER_GO_ON; - + if (p->config_storage) { size_t i; for (i = 0; i < srv->config_context->used; i++) { plugin_config *s = p->config_storage[i]; if (!s) continue; - + buffer_free(s->db_filename); buffer_free(s->download_url); buffer_free(s->trigger_url); buffer_free(s->deny_url); - + buffer_free(s->mc_namespace); array_free(s->mc_hosts); - + #if defined(HAVE_PCRE_H) if (s->trigger_regex) pcre_free(s->trigger_regex); if (s->download_regex) pcre_free(s->download_regex); @@ -108,16 +109,16 @@ FREE_FUNC(mod_trigger_b4_dl_free) { #if defined(HAVE_MEMCACHE_H) if (s->mc) mc_free(s->mc); #endif - + free(s); } free(p->config_storage); } - + buffer_free(p->tmp_buf); - + free(p); - + return HANDLER_GO_ON; } @@ -126,9 +127,9 @@ FREE_FUNC(mod_trigger_b4_dl_free) { SETDEFAULTS_FUNC(mod_trigger_b4_dl_set_defaults) { plugin_data *p = p_d; size_t i = 0; - - - config_values_t cv[] = { + + + config_values_t cv[] = { { "trigger-before-download.gdbm-filename", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 0 */ { "trigger-before-download.trigger-url", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 1 */ { "trigger-before-download.download-url", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 2 */ @@ -139,18 +140,18 @@ SETDEFAULTS_FUNC(mod_trigger_b4_dl_set_defaults) { { "trigger-before-download.debug", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 7 */ { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET } }; - + if (!p) return HANDLER_ERROR; - + p->config_storage = calloc(1, srv->config_context->used * sizeof(specific_config *)); - + for (i = 0; i < srv->config_context->used; i++) { plugin_config *s; #if defined(HAVE_PCRE_H) const char *errptr; int erroff; #endif - + s = calloc(1, sizeof(plugin_config)); s->db_filename = buffer_init(); s->download_url = buffer_init(); @@ -158,7 +159,7 @@ SETDEFAULTS_FUNC(mod_trigger_b4_dl_set_defaults) { s->deny_url = buffer_init(); s->mc_hosts = array_init(); s->mc_namespace = buffer_init(); - + cv[0].destination = s->db_filename; cv[1].destination = s->trigger_url; cv[2].destination = s->download_url; @@ -167,41 +168,44 @@ SETDEFAULTS_FUNC(mod_trigger_b4_dl_set_defaults) { cv[5].destination = s->mc_hosts; cv[6].destination = s->mc_namespace; cv[7].destination = &(s->debug); - + p->config_storage[i] = s; - + if (0 != config_insert_values_global(srv, ((data_config *)srv->config_context->data[i])->value, cv)) { return HANDLER_ERROR; } #if defined(HAVE_GDBM_H) if (!buffer_is_empty(s->db_filename)) { if (NULL == (s->db = gdbm_open(s->db_filename->ptr, 4096, GDBM_WRCREAT | GDBM_NOLOCK, S_IRUSR | S_IWUSR, 0))) { - log_error_write(srv, __FILE__, __LINE__, "s", + log_error_write(srv, __FILE__, __LINE__, "s", "gdbm-open failed"); return HANDLER_ERROR; } +#ifdef FD_CLOEXEC + fcntl(gdbm_fdesc(s->db), F_SETFD, FD_CLOEXEC); +#endif } #endif -#if defined(HAVE_PCRE_H) +#if defined(HAVE_PCRE_H) if (!buffer_is_empty(s->download_url)) { if (NULL == (s->download_regex = pcre_compile(s->download_url->ptr, 0, &errptr, &erroff, NULL))) { - - log_error_write(srv, __FILE__, __LINE__, "sbss", - "compiling regex for download-url failed:", + + log_error_write(srv, __FILE__, __LINE__, "sbss", + "compiling regex for download-url failed:", s->download_url, "pos:", erroff); return HANDLER_ERROR; } } - + if (!buffer_is_empty(s->trigger_url)) { if (NULL == (s->trigger_regex = pcre_compile(s->trigger_url->ptr, 0, &errptr, &erroff, NULL))) { - - log_error_write(srv, __FILE__, __LINE__, "sbss", - "compiling regex for trigger-url failed:", + + log_error_write(srv, __FILE__, __LINE__, "sbss", + "compiling regex for trigger-url failed:", s->trigger_url, "pos:", erroff); - + return HANDLER_ERROR; } } @@ -211,33 +215,33 @@ SETDEFAULTS_FUNC(mod_trigger_b4_dl_set_defaults) { #if defined(HAVE_MEMCACHE_H) size_t k; s->mc = mc_new(); - + for (k = 0; k < s->mc_hosts->used; k++) { data_string *ds = (data_string *)s->mc_hosts->data[k]; - + if (0 != mc_server_add4(s->mc, ds->value->ptr)) { - log_error_write(srv, __FILE__, __LINE__, "sb", - "connection to host failed:", + log_error_write(srv, __FILE__, __LINE__, "sb", + "connection to host failed:", ds->value); - + return HANDLER_ERROR; } } #else - log_error_write(srv, __FILE__, __LINE__, "s", + log_error_write(srv, __FILE__, __LINE__, "s", "memcache support is not compiled in but trigger-before-download.memcache-hosts is set, aborting"); return HANDLER_ERROR; #endif } - + #if (!defined(HAVE_GDBM_H) && !defined(HAVE_MEMCACHE_H)) || !defined(HAVE_PCRE_H) - log_error_write(srv, __FILE__, __LINE__, "s", + log_error_write(srv, __FILE__, __LINE__, "s", "(either gdbm or libmemcache) and pcre are require, but were not found, aborting"); return HANDLER_ERROR; #endif } - + return HANDLER_GO_ON; } @@ -246,14 +250,14 @@ SETDEFAULTS_FUNC(mod_trigger_b4_dl_set_defaults) { static int mod_trigger_b4_dl_patch_connection(server *srv, connection *con, plugin_data *p) { size_t i, j; plugin_config *s = p->config_storage[0]; - + #if defined(HAVE_GDBM) PATCH(db); -#endif +#endif #if defined(HAVE_PCRE_H) PATCH(download_regex); PATCH(trigger_regex); -#endif +#endif PATCH(trigger_timeout); PATCH(deny_url); PATCH(mc_namespace); @@ -261,15 +265,15 @@ static int mod_trigger_b4_dl_patch_connection(server *srv, connection *con, plug #if defined(HAVE_MEMCACHE_H) PATCH(mc); #endif - + /* skip the first, the global context */ for (i = 1; i < srv->config_context->used; i++) { data_config *dc = (data_config *)srv->config_context->data[i]; s = p->config_storage[i]; - + /* condition didn't match */ if (!config_check_cond(srv, con, dc)) continue; - + /* merge config */ for (j = 0; j < dc->value->used; j++) { data_unset *du = dc->value->data[j]; @@ -301,7 +305,7 @@ static int mod_trigger_b4_dl_patch_connection(server *srv, connection *con, plug } } } - + return 0; } #undef PATCH @@ -315,20 +319,22 @@ URIHANDLER_FUNC(mod_trigger_b4_dl_uri_handler) { int n; # define N 10 int ovec[N * 3]; - + + if (con->mode != DIRECT) return HANDLER_GO_ON; + if (con->uri.path->used == 0) return HANDLER_GO_ON; - + mod_trigger_b4_dl_patch_connection(srv, con, p); - + if (!p->conf.trigger_regex || !p->conf.download_regex) return HANDLER_GO_ON; - + # if !defined(HAVE_GDBM_H) && !defined(HAVE_MEMCACHE_H) return HANDLER_GO_ON; # elif defined(HAVE_GDBM_H) && defined(HAVE_MEMCACHE_H) if (!p->conf.db && !p->conf.mc) return HANDLER_GO_ON; if (p->conf.db && p->conf.mc) { /* can't decide which one */ - + return HANDLER_GO_ON; } # elif defined(HAVE_GDBM_H) @@ -336,12 +342,12 @@ URIHANDLER_FUNC(mod_trigger_b4_dl_uri_handler) { # else if (!p->conf.mc) return HANDLER_GO_ON; # endif - + if (NULL != (ds = (data_string *)array_get_element(con->request.headers, "X-Forwarded-For"))) { /* X-Forwarded-For contains the ip behind the proxy */ - + remote_ip = ds->value->ptr; - + /* memcache can't handle spaces */ } else { remote_ip = inet_ntop_cache_get_ip(srv, &(con->dst_addr)); @@ -350,13 +356,13 @@ URIHANDLER_FUNC(mod_trigger_b4_dl_uri_handler) { if (p->conf.debug) { log_error_write(srv, __FILE__, __LINE__, "ss", "(debug) remote-ip:", remote_ip); } - + /* check if URL is a trigger -> insert IP into DB */ if ((n = pcre_exec(p->conf.trigger_regex, NULL, con->uri.path->ptr, con->uri.path->used - 1, 0, 0, ovec, 3 * N)) < 0) { if (n != PCRE_ERROR_NOMATCH) { log_error_write(srv, __FILE__, __LINE__, "sd", "execution error while matching:", n); - + return HANDLER_ERROR; } } else { @@ -364,34 +370,34 @@ URIHANDLER_FUNC(mod_trigger_b4_dl_uri_handler) { if (p->conf.db) { /* the trigger matched */ datum key, val; - + key.dptr = (char *)remote_ip; key.dsize = strlen(remote_ip); - + val.dptr = (char *)&(srv->cur_ts); val.dsize = sizeof(srv->cur_ts); - + if (0 != gdbm_store(p->conf.db, key, val, GDBM_REPLACE)) { log_error_write(srv, __FILE__, __LINE__, "s", "insert failed"); } } # endif -# if defined(HAVE_MEMCACHE_H) +# if defined(HAVE_MEMCACHE_H) if (p->conf.mc) { size_t i; buffer_copy_string_buffer(p->tmp_buf, p->conf.mc_namespace); buffer_append_string(p->tmp_buf, remote_ip); - + for (i = 0; i < p->tmp_buf->used - 1; i++) { if (p->tmp_buf->ptr[i] == ' ') p->tmp_buf->ptr[i] = '-'; } - + if (p->conf.debug) { log_error_write(srv, __FILE__, __LINE__, "sb", "(debug) triggered IP:", p->tmp_buf); } - if (0 != mc_set(p->conf.mc, + if (0 != mc_set(p->conf.mc, CONST_BUF_LEN(p->tmp_buf), (char *)&(srv->cur_ts), sizeof(srv->cur_ts), p->conf.trigger_timeout, 0)) { @@ -401,7 +407,7 @@ URIHANDLER_FUNC(mod_trigger_b4_dl_uri_handler) { } # endif } - + /* check if URL is a download -> check IP in DB, update timestamp */ if ((n = pcre_exec(p->conf.download_regex, NULL, con->uri.path->ptr, con->uri.path->used - 1, 0, 0, ovec, 3 * N)) < 0) { if (n != PCRE_ERROR_NOMATCH) { @@ -411,93 +417,95 @@ URIHANDLER_FUNC(mod_trigger_b4_dl_uri_handler) { } } else { /* the download uri matched */ -# if defined(HAVE_GDBM_H) +# if defined(HAVE_GDBM_H) if (p->conf.db) { datum key, val; time_t last_hit; - + key.dptr = (char *)remote_ip; key.dsize = strlen(remote_ip); - + val = gdbm_fetch(p->conf.db, key); - + if (val.dptr == NULL) { /* not found, redirect */ - + response_header_insert(srv, con, CONST_STR_LEN("Location"), CONST_BUF_LEN(p->conf.deny_url)); - con->http_status = 307; - + con->file_finished = 1; + return HANDLER_FINISHED; } - + last_hit = *(time_t *)(val.dptr); - + free(val.dptr); - + if (srv->cur_ts - last_hit > p->conf.trigger_timeout) { /* found, but timeout, redirect */ - + response_header_insert(srv, con, CONST_STR_LEN("Location"), CONST_BUF_LEN(p->conf.deny_url)); con->http_status = 307; - + con->file_finished = 1; + if (p->conf.db) { if (0 != gdbm_delete(p->conf.db, key)) { log_error_write(srv, __FILE__, __LINE__, "s", "delete failed"); } } - + return HANDLER_FINISHED; } - + val.dptr = (char *)&(srv->cur_ts); val.dsize = sizeof(srv->cur_ts); - + if (0 != gdbm_store(p->conf.db, key, val, GDBM_REPLACE)) { log_error_write(srv, __FILE__, __LINE__, "s", "insert failed"); } } # endif - -# if defined(HAVE_MEMCACHE_H) + +# if defined(HAVE_MEMCACHE_H) if (p->conf.mc) { void *r; size_t i; - + buffer_copy_string_buffer(p->tmp_buf, p->conf.mc_namespace); buffer_append_string(p->tmp_buf, remote_ip); - + for (i = 0; i < p->tmp_buf->used - 1; i++) { if (p->tmp_buf->ptr[i] == ' ') p->tmp_buf->ptr[i] = '-'; } - + if (p->conf.debug) { log_error_write(srv, __FILE__, __LINE__, "sb", "(debug) checking IP:", p->tmp_buf); } /** - * + * * memcached is do expiration for us, as long as we can fetch it every thing is ok - * and the timestamp is updated - * + * and the timestamp is updated + * */ - if (NULL == (r = mc_aget(p->conf.mc, + if (NULL == (r = mc_aget(p->conf.mc, CONST_BUF_LEN(p->tmp_buf) ))) { - + response_header_insert(srv, con, CONST_STR_LEN("Location"), CONST_BUF_LEN(p->conf.deny_url)); - + con->http_status = 307; - + con->file_finished = 1; + return HANDLER_FINISHED; } - + free(r); - + /* set a new timeout */ - if (0 != mc_set(p->conf.mc, + if (0 != mc_set(p->conf.mc, CONST_BUF_LEN(p->tmp_buf), (char *)&(srv->cur_ts), sizeof(srv->cur_ts), p->conf.trigger_timeout, 0)) { @@ -507,13 +515,13 @@ URIHANDLER_FUNC(mod_trigger_b4_dl_uri_handler) { } # endif } - + #else UNUSED(srv); UNUSED(con); UNUSED(p_d); #endif - + return HANDLER_GO_ON; } @@ -521,21 +529,21 @@ URIHANDLER_FUNC(mod_trigger_b4_dl_uri_handler) { TRIGGER_FUNC(mod_trigger_b4_dl_handle_trigger) { plugin_data *p = p_d; size_t i; - + /* check DB each minute */ if (srv->cur_ts % 60 != 0) return HANDLER_GO_ON; - + /* cleanup */ for (i = 0; i < srv->config_context->used; i++) { plugin_config *s = p->config_storage[i]; datum key, val, okey; - + if (!s->db) continue; - + okey.dptr = NULL; - - /* according to the manual this loop + delete does delete all entries on its way - * + + /* according to the manual this loop + delete does delete all entries on its way + * * we don't care as the next round will remove them. We don't have to perfect here. */ for (key = gdbm_firstkey(s->db); key.dptr; key = gdbm_nextkey(s->db, okey)) { @@ -544,21 +552,21 @@ TRIGGER_FUNC(mod_trigger_b4_dl_handle_trigger) { free(okey.dptr); okey.dptr = NULL; } - + val = gdbm_fetch(s->db, key); - + last_hit = *(time_t *)(val.dptr); - + free(val.dptr); - + if (srv->cur_ts - last_hit > s->trigger_timeout) { gdbm_delete(s->db, key); } - + okey = key; } if (okey.dptr) free(okey.dptr); - + /* reorg once a day */ if ((srv->cur_ts % (60 * 60 * 24) != 0)) gdbm_reorganize(s->db); } @@ -568,10 +576,11 @@ TRIGGER_FUNC(mod_trigger_b4_dl_handle_trigger) { /* this function is called at dlopen() time and inits the callbacks */ +int mod_trigger_b4_dl_plugin_init(plugin *p); int mod_trigger_b4_dl_plugin_init(plugin *p) { p->version = LIGHTTPD_VERSION_ID; p->name = buffer_init_string("trigger_b4_dl"); - + p->init = mod_trigger_b4_dl_init; p->handle_uri_clean = mod_trigger_b4_dl_uri_handler; p->set_defaults = mod_trigger_b4_dl_set_defaults; @@ -579,8 +588,8 @@ int mod_trigger_b4_dl_plugin_init(plugin *p) { p->handle_trigger = mod_trigger_b4_dl_handle_trigger; #endif p->cleanup = mod_trigger_b4_dl_free; - + p->data = NULL; - + return 0; } diff --git a/src/mod_userdir.c b/src/mod_userdir.c index 9612fa8..4a1967f 100644 --- a/src/mod_userdir.c +++ b/src/mod_userdir.c @@ -1,8 +1,3 @@ -#include <sys/types.h> - -#include <stdlib.h> -#include <string.h> - #include "base.h" #include "log.h" #include "buffer.h" @@ -11,8 +6,13 @@ #include "plugin.h" +#include <sys/types.h> + +#include <stdlib.h> +#include <string.h> + #ifdef HAVE_PWD_H -#include <pwd.h> +# include <pwd.h> #endif /* plugin config for all request/connections */ @@ -21,58 +21,59 @@ typedef struct { array *include_user; buffer *path; buffer *basepath; + unsigned short letterhomes; } plugin_config; typedef struct { PLUGIN_DATA; - + buffer *username; buffer *temp_path; - + plugin_config **config_storage; - - plugin_config conf; + + plugin_config conf; } plugin_data; /* init the plugin data */ INIT_FUNC(mod_userdir_init) { plugin_data *p; - + p = calloc(1, sizeof(*p)); - + p->username = buffer_init(); p->temp_path = buffer_init(); - + return p; } /* detroy the plugin data */ FREE_FUNC(mod_userdir_free) { plugin_data *p = p_d; - + if (!p) return HANDLER_GO_ON; - + if (p->config_storage) { size_t i; - + for (i = 0; i < srv->config_context->used; i++) { plugin_config *s = p->config_storage[i]; - + array_free(s->include_user); array_free(s->exclude_user); buffer_free(s->path); buffer_free(s->basepath); - + free(s); } free(p->config_storage); } - + buffer_free(p->username); buffer_free(p->temp_path); - + free(p); - + return HANDLER_GO_ON; } @@ -81,40 +82,43 @@ FREE_FUNC(mod_userdir_free) { SETDEFAULTS_FUNC(mod_userdir_set_defaults) { plugin_data *p = p_d; size_t i; - - config_values_t cv[] = { + + config_values_t cv[] = { { "userdir.path", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 0 */ { "userdir.exclude-user", NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION }, /* 1 */ { "userdir.include-user", NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION }, /* 2 */ { "userdir.basepath", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 3 */ + { "userdir.letterhomes", NULL, T_CONFIG_BOOLEAN,T_CONFIG_SCOPE_CONNECTION }, /* 4 */ { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET } }; - + if (!p) return HANDLER_ERROR; - + p->config_storage = calloc(1, srv->config_context->used * sizeof(specific_config *)); - + for (i = 0; i < srv->config_context->used; i++) { plugin_config *s; - + s = calloc(1, sizeof(plugin_config)); s->exclude_user = array_init(); s->include_user = array_init(); s->path = buffer_init(); s->basepath = buffer_init(); - + s->letterhomes = 0; + cv[0].destination = s->path; cv[1].destination = s->exclude_user; cv[2].destination = s->include_user; cv[3].destination = s->basepath; - + cv[4].destination = &(s->letterhomes); + p->config_storage[i] = s; - + if (0 != config_insert_values_global(srv, ((data_config *)srv->config_context->data[i])->value, cv)) { return HANDLER_ERROR; } } - + return HANDLER_GO_ON; } @@ -123,24 +127,25 @@ SETDEFAULTS_FUNC(mod_userdir_set_defaults) { static int mod_userdir_patch_connection(server *srv, connection *con, plugin_data *p) { size_t i, j; plugin_config *s = p->config_storage[0]; - + PATCH(path); PATCH(exclude_user); PATCH(include_user); PATCH(basepath); - + PATCH(letterhomes); + /* skip the first, the global context */ for (i = 1; i < srv->config_context->used; i++) { data_config *dc = (data_config *)srv->config_context->data[i]; s = p->config_storage[i]; - + /* condition didn't match */ if (!config_check_cond(srv, con, dc)) continue; - + /* merge config */ for (j = 0; j < dc->value->used; j++) { data_unset *du = dc->value->data[j]; - + if (buffer_is_equal_string(du->key, CONST_STR_LEN("userdir.path"))) { PATCH(path); } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("userdir.exclude-user"))) { @@ -149,17 +154,18 @@ static int mod_userdir_patch_connection(server *srv, connection *con, plugin_dat PATCH(include_user); } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("userdir.basepath"))) { PATCH(basepath); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("userdir.letterhomes"))) { + PATCH(letterhomes); } } } - + return 0; } #undef PATCH URIHANDLER_FUNC(mod_userdir_docroot_handler) { plugin_data *p = p_d; - int uri_len; size_t k; char *rel_url; #ifdef HAVE_PWD_H @@ -169,18 +175,21 @@ URIHANDLER_FUNC(mod_userdir_docroot_handler) { if (con->uri.path->used == 0) return HANDLER_GO_ON; mod_userdir_patch_connection(srv, con, p); - - uri_len = con->uri.path->used - 1; - + + /* enforce the userdir.path to be set in the config, ugly fix for #1587; + * should be replaced with a clean .enabled option in 1.5 + */ + if (p->conf.path->used == 0) return HANDLER_GO_ON; + /* /~user/foo.html -> /home/user/public_html/foo.html */ - + if (con->uri.path->ptr[0] != '/' || con->uri.path->ptr[1] != '~') return HANDLER_GO_ON; - + if (NULL == (rel_url = strchr(con->uri.path->ptr + 2, '/'))) { /* / is missing -> redirect to .../ as we are a user - DIRECTORY ! :) */ http_response_redirect_to_directory(srv, con); - + return HANDLER_FINISHED; } @@ -188,10 +197,10 @@ URIHANDLER_FUNC(mod_userdir_docroot_handler) { if (0 == rel_url - (con->uri.path->ptr + 2)) { return HANDLER_GO_ON; } - + buffer_copy_string_len(p->username, con->uri.path->ptr + 2, rel_url - (con->uri.path->ptr + 2)); - - if (buffer_is_empty(p->conf.basepath) + + if (buffer_is_empty(p->conf.basepath) #ifdef HAVE_PWD_H && NULL == (pwd = getpwnam(p->username->ptr)) #endif @@ -200,31 +209,31 @@ URIHANDLER_FUNC(mod_userdir_docroot_handler) { return HANDLER_GO_ON; } - + for (k = 0; k < p->conf.exclude_user->used; k++) { data_string *ds = (data_string *)p->conf.exclude_user->data[k]; - + if (buffer_is_equal(ds->value, p->username)) { /* user in exclude list */ return HANDLER_GO_ON; } } - + if (p->conf.include_user->used) { int found_user = 0; for (k = 0; k < p->conf.include_user->used; k++) { data_string *ds = (data_string *)p->conf.include_user->data[k]; - + if (buffer_is_equal(ds->value, p->username)) { /* user in include list */ found_user = 1; break; } } - + if (!found_user) return HANDLER_GO_ON; } - + /* we build the physical path */ if (buffer_is_empty(p->conf.basepath)) { @@ -250,26 +259,49 @@ URIHANDLER_FUNC(mod_userdir_docroot_handler) { return HANDLER_GO_ON; } } + if (con->conf.force_lowercase_filenames) { + buffer_to_lower(p->username); + } buffer_copy_string_buffer(p->temp_path, p->conf.basepath); BUFFER_APPEND_SLASH(p->temp_path); + if (p->conf.letterhomes) { + buffer_append_string_len(p->temp_path, p->username->ptr, 1); + BUFFER_APPEND_SLASH(p->temp_path); + } buffer_append_string_buffer(p->temp_path, p->username); } BUFFER_APPEND_SLASH(p->temp_path); - buffer_append_string_buffer(p->temp_path, p->conf.path); + buffer_append_string_buffer(p->temp_path, p->conf.path); if (buffer_is_empty(p->conf.basepath)) { struct stat st; int ret; - + ret = stat(p->temp_path->ptr, &st); if (ret < 0 || S_ISDIR(st.st_mode) != 1) { return HANDLER_GO_ON; - } + } } + /* the physical rel_path is basically the same as uri.path; + * but it is converted to lowercase in case of force_lowercase_filenames and some special handling + * for trailing '.', ' ' and '/' on windows + * we assume that no docroot/physical handler changed this + * (docroot should only set the docroot/server name, phyiscal should only change the phyiscal.path; + * the exception mod_secure_download doesn't work with userdir anyway) + */ BUFFER_APPEND_SLASH(p->temp_path); - buffer_append_string(p->temp_path, rel_url + 1); /* skip the / */ + /* if no second '/' is found, we assume that it was stripped from the uri.path for the special handling + * on windows. + * we do not care about the trailing slash here on windows, as we already ensured it is a directory + * + * TODO: what to do with trailing dots in usernames on windows? they may result in the same directory + * as a username without them. + */ + if (NULL != (rel_url = strchr(con->physical.rel_path->ptr + 2, '/'))) { + buffer_append_string(p->temp_path, rel_url + 1); /* skip the / */ + } buffer_copy_string_buffer(con->physical.path, p->temp_path); buffer_reset(p->temp_path); @@ -279,16 +311,17 @@ URIHANDLER_FUNC(mod_userdir_docroot_handler) { /* this function is called at dlopen() time and inits the callbacks */ +int mod_userdir_plugin_init(plugin *p); int mod_userdir_plugin_init(plugin *p) { p->version = LIGHTTPD_VERSION_ID; p->name = buffer_init_string("userdir"); - + p->init = mod_userdir_init; p->handle_physical = mod_userdir_docroot_handler; p->set_defaults = mod_userdir_set_defaults; p->cleanup = mod_userdir_free; - + p->data = NULL; - + return 0; } diff --git a/src/mod_usertrack.c b/src/mod_usertrack.c index 25fb8ec..9ecabdc 100644 --- a/src/mod_usertrack.c +++ b/src/mod_usertrack.c @@ -1,67 +1,63 @@ -#include <ctype.h> -#include <stdlib.h> -#include <string.h> - #include "base.h" #include "log.h" #include "buffer.h" #include "plugin.h" -#ifdef USE_OPENSSL -# include <openssl/md5.h> -#else -# include "md5.h" -#endif +#include <ctype.h> +#include <stdlib.h> +#include <string.h> + +#include "md5.h" /* plugin config for all request/connections */ typedef struct { buffer *cookie_name; buffer *cookie_domain; - unsigned short cookie_max_age; + unsigned int cookie_max_age; } plugin_config; typedef struct { PLUGIN_DATA; - + plugin_config **config_storage; - - plugin_config conf; + + plugin_config conf; } plugin_data; /* init the plugin data */ INIT_FUNC(mod_usertrack_init) { plugin_data *p; - + p = calloc(1, sizeof(*p)); - + return p; } /* detroy the plugin data */ FREE_FUNC(mod_usertrack_free) { plugin_data *p = p_d; - + UNUSED(srv); - + if (!p) return HANDLER_GO_ON; - + if (p->config_storage) { size_t i; for (i = 0; i < srv->config_context->used; i++) { plugin_config *s = p->config_storage[i]; - + buffer_free(s->cookie_name); buffer_free(s->cookie_domain); - + free(s); } free(p->config_storage); } - + free(p); - + return HANDLER_GO_ON; } @@ -70,69 +66,69 @@ FREE_FUNC(mod_usertrack_free) { SETDEFAULTS_FUNC(mod_usertrack_set_defaults) { plugin_data *p = p_d; size_t i = 0; - - config_values_t cv[] = { + + config_values_t cv[] = { { "usertrack.cookie-name", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 0 */ - { "usertrack.cookie-max-age", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 1 */ + { "usertrack.cookie-max-age", NULL, T_CONFIG_INT, T_CONFIG_SCOPE_CONNECTION }, /* 1 */ { "usertrack.cookie-domain", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 2 */ - - { "usertrack.cookiename", NULL, T_CONFIG_DEPRECATED, T_CONFIG_SCOPE_CONNECTION }, + + { "usertrack.cookiename", NULL, T_CONFIG_DEPRECATED, T_CONFIG_SCOPE_CONNECTION }, { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET } }; - + if (!p) return HANDLER_ERROR; - + p->config_storage = calloc(1, srv->config_context->used * sizeof(specific_config *)); - + for (i = 0; i < srv->config_context->used; i++) { plugin_config *s; - + s = calloc(1, sizeof(plugin_config)); s->cookie_name = buffer_init(); s->cookie_domain = buffer_init(); s->cookie_max_age = 0; - + cv[0].destination = s->cookie_name; cv[1].destination = &(s->cookie_max_age); cv[2].destination = s->cookie_domain; - + p->config_storage[i] = s; - + if (0 != config_insert_values_global(srv, ((data_config *)srv->config_context->data[i])->value, cv)) { return HANDLER_ERROR; } - + if (buffer_is_empty(s->cookie_name)) { - buffer_copy_string(s->cookie_name, "TRACKID"); + buffer_copy_string_len(s->cookie_name, CONST_STR_LEN("TRACKID")); } else { size_t j; for (j = 0; j < s->cookie_name->used - 1; j++) { char c = s->cookie_name->ptr[j] | 32; if (c < 'a' || c > 'z') { - log_error_write(srv, __FILE__, __LINE__, "sb", - "invalid character in usertrack.cookie-name:", + log_error_write(srv, __FILE__, __LINE__, "sb", + "invalid character in usertrack.cookie-name:", s->cookie_name); - + return HANDLER_ERROR; } } } - + if (!buffer_is_empty(s->cookie_domain)) { size_t j; for (j = 0; j < s->cookie_domain->used - 1; j++) { char c = s->cookie_domain->ptr[j]; if (c <= 32 || c >= 127 || c == '"' || c == '\\') { - log_error_write(srv, __FILE__, __LINE__, "sb", - "invalid character in usertrack.cookie-domain:", + log_error_write(srv, __FILE__, __LINE__, "sb", + "invalid character in usertrack.cookie-domain:", s->cookie_domain); - + return HANDLER_ERROR; } } } } - + return HANDLER_GO_ON; } @@ -141,23 +137,23 @@ SETDEFAULTS_FUNC(mod_usertrack_set_defaults) { static int mod_usertrack_patch_connection(server *srv, connection *con, plugin_data *p) { size_t i, j; plugin_config *s = p->config_storage[0]; - + PATCH(cookie_name); PATCH(cookie_domain); PATCH(cookie_max_age); - + /* skip the first, the global context */ for (i = 1; i < srv->config_context->used; i++) { data_config *dc = (data_config *)srv->config_context->data[i]; s = p->config_storage[i]; - + /* condition didn't match */ if (!config_check_cond(srv, con, dc)) continue; - + /* merge config */ for (j = 0; j < dc->value->used; j++) { data_unset *du = dc->value->data[j]; - + if (buffer_is_equal_string(du->key, CONST_STR_LEN("usertrack.cookie-name"))) { PATCH(cookie_name); } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("usertrack.cookie-max-age"))) { @@ -167,7 +163,7 @@ static int mod_usertrack_patch_connection(server *srv, connection *con, plugin_d } } } - + return 0; } #undef PATCH @@ -176,96 +172,97 @@ URIHANDLER_FUNC(mod_usertrack_uri_handler) { plugin_data *p = p_d; data_string *ds; unsigned char h[16]; - MD5_CTX Md5Ctx; + li_MD5_CTX Md5Ctx; char hh[32]; - + if (con->uri.path->used == 0) return HANDLER_GO_ON; - + mod_usertrack_patch_connection(srv, con, p); - + if (NULL != (ds = (data_string *)array_get_element(con->request.headers, "Cookie"))) { char *g; /* we have a cookie, does it contain a valid name ? */ - - /* parse the cookie - * + + /* parse the cookie + * * check for cookiename + (WS | '=') - * + * */ - + if (NULL != (g = strstr(ds->value->ptr, p->conf.cookie_name->ptr))) { char *nc; - + /* skip WS */ for (nc = g + p->conf.cookie_name->used-1; *nc == ' ' || *nc == '\t'; nc++); - + if (*nc == '=') { /* ok, found the key of our own cookie */ - + if (strlen(nc) > 32) { /* i'm lazy */ return HANDLER_GO_ON; } } } - } - + } + /* set a cookie */ if (NULL == (ds = (data_string *)array_get_unused_element(con->response.headers, TYPE_STRING))) { ds = data_response_init(); } - buffer_copy_string(ds->key, "Set-Cookie"); + buffer_copy_string_len(ds->key, CONST_STR_LEN("Set-Cookie")); buffer_copy_string_buffer(ds->value, p->conf.cookie_name); - buffer_append_string(ds->value, "=\""); - + buffer_append_string_len(ds->value, CONST_STR_LEN("=")); + /* taken from mod_auth.c */ - + /* generate shared-secret */ - MD5_Init(&Md5Ctx); - MD5_Update(&Md5Ctx, (unsigned char *)con->uri.path->ptr, con->uri.path->used - 1); - MD5_Update(&Md5Ctx, (unsigned char *)"+", 1); - + li_MD5_Init(&Md5Ctx); + li_MD5_Update(&Md5Ctx, (unsigned char *)con->uri.path->ptr, con->uri.path->used - 1); + li_MD5_Update(&Md5Ctx, (unsigned char *)"+", 1); + /* we assume sizeof(time_t) == 4 here, but if not it ain't a problem at all */ - ltostr(hh, srv->cur_ts); - MD5_Update(&Md5Ctx, (unsigned char *)hh, strlen(hh)); - ltostr(hh, rand()); - MD5_Update(&Md5Ctx, (unsigned char *)hh, strlen(hh)); - - MD5_Final(h, &Md5Ctx); - + LI_ltostr(hh, srv->cur_ts); + li_MD5_Update(&Md5Ctx, (unsigned char *)hh, strlen(hh)); + li_MD5_Update(&Md5Ctx, (unsigned char *)srv->entropy, sizeof(srv->entropy)); + LI_ltostr(hh, rand()); + li_MD5_Update(&Md5Ctx, (unsigned char *)hh, strlen(hh)); + + li_MD5_Final(h, &Md5Ctx); + buffer_append_string_encoded(ds->value, (char *)h, 16, ENCODING_HEX); - buffer_append_string(ds->value, "\"; Path=\"/\""); - buffer_append_string(ds->value, "; Version=\"1\""); - + buffer_append_string_len(ds->value, CONST_STR_LEN("; Path=/")); + buffer_append_string_len(ds->value, CONST_STR_LEN("; Version=1")); + if (!buffer_is_empty(p->conf.cookie_domain)) { - buffer_append_string(ds->value, "; Domain=\""); - buffer_append_string_buffer(ds->value, p->conf.cookie_domain); - buffer_append_string(ds->value, "\""); + buffer_append_string_len(ds->value, CONST_STR_LEN("; Domain=")); + buffer_append_string_encoded(ds->value, CONST_BUF_LEN(p->conf.cookie_domain), ENCODING_REL_URI); } - + if (p->conf.cookie_max_age) { - buffer_append_string(ds->value, "; max-age="); + buffer_append_string_len(ds->value, CONST_STR_LEN("; max-age=")); buffer_append_long(ds->value, p->conf.cookie_max_age); } - + array_insert_unique(con->response.headers, (data_unset *)ds); - + return HANDLER_GO_ON; } /* this function is called at dlopen() time and inits the callbacks */ +int mod_usertrack_plugin_init(plugin *p); int mod_usertrack_plugin_init(plugin *p) { p->version = LIGHTTPD_VERSION_ID; p->name = buffer_init_string("usertrack"); - + p->init = mod_usertrack_init; p->handle_uri_clean = mod_usertrack_uri_handler; p->set_defaults = mod_usertrack_set_defaults; p->cleanup = mod_usertrack_free; - + p->data = NULL; - + return 0; } diff --git a/src/mod_webdav.c b/src/mod_webdav.c index 3306c73..2231ab8 100644 --- a/src/mod_webdav.c +++ b/src/mod_webdav.c @@ -1,19 +1,27 @@ +#include "base.h" +#include "log.h" +#include "buffer.h" +#include "response.h" + +#include "plugin.h" + +#include "stream.h" +#include "stat_cache.h" + +#include "sys-mmap.h" + #include <sys/types.h> #include <sys/stat.h> #include <ctype.h> #include <stdlib.h> #include <string.h> -#include <dirent.h> #include <errno.h> -#include <unistd.h> #include <fcntl.h> #include <stdio.h> #include <assert.h> -#include <sys/mman.h> -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif +#include <unistd.h> +#include <dirent.h> #if defined(HAVE_LIBXML_H) && defined(HAVE_SQLITE3_H) #define USE_PROPPATCH @@ -23,26 +31,21 @@ #include <sqlite3.h> #endif -#include "base.h" -#include "log.h" -#include "buffer.h" -#include "response.h" - -#include "plugin.h" - -#include "stream.h" -#include "stat_cache.h" - +#if defined(HAVE_LIBXML_H) && defined(HAVE_SQLITE3_H) && defined(HAVE_UUID_UUID_H) +#define USE_LOCKS +#include <uuid/uuid.h> +#endif /** * this is a webdav for a lighttpd plugin * - * at least a very basic one. + * at least a very basic one. * - for now it is read-only and we only support PROPFIND - * + * */ - +#define WEBDAV_FILE_MODE S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH +#define WEBDAV_DIR_MODE S_IRWXU | S_IRWXG | S_IRWXO /* plugin config for all request/connections */ @@ -58,64 +61,70 @@ typedef struct { sqlite3_stmt *stmt_delete_prop; sqlite3_stmt *stmt_select_prop; sqlite3_stmt *stmt_select_propnames; - + sqlite3_stmt *stmt_delete_uri; sqlite3_stmt *stmt_move_uri; sqlite3_stmt *stmt_copy_uri; + + sqlite3_stmt *stmt_remove_lock; + sqlite3_stmt *stmt_create_lock; + sqlite3_stmt *stmt_read_lock; + sqlite3_stmt *stmt_read_lock_by_uri; + sqlite3_stmt *stmt_refresh_lock; #endif } plugin_config; typedef struct { PLUGIN_DATA; - + buffer *tmp_buf; request_uri uri; physical physical; plugin_config **config_storage; - - plugin_config conf; + + plugin_config conf; } plugin_data; /* init the plugin data */ INIT_FUNC(mod_webdav_init) { plugin_data *p; - + p = calloc(1, sizeof(*p)); - + p->tmp_buf = buffer_init(); p->uri.scheme = buffer_init(); p->uri.path_raw = buffer_init(); p->uri.path = buffer_init(); p->uri.authority = buffer_init(); - + p->physical.path = buffer_init(); p->physical.rel_path = buffer_init(); p->physical.doc_root = buffer_init(); p->physical.basedir = buffer_init(); - + return p; } /* detroy the plugin data */ FREE_FUNC(mod_webdav_free) { plugin_data *p = p_d; - + UNUSED(srv); if (!p) return HANDLER_GO_ON; - + if (p->config_storage) { size_t i; for (i = 0; i < srv->config_context->used; i++) { plugin_config *s = p->config_storage[i]; if (!s) continue; - + buffer_free(s->sqlite_db_name); #ifdef USE_PROPPATCH - if (s->sql) { + if (s->sql) { sqlite3_finalize(s->stmt_delete_prop); sqlite3_finalize(s->stmt_delete_uri); sqlite3_finalize(s->stmt_copy_uri); @@ -123,9 +132,15 @@ FREE_FUNC(mod_webdav_free) { sqlite3_finalize(s->stmt_update_prop); sqlite3_finalize(s->stmt_select_prop); sqlite3_finalize(s->stmt_select_propnames); + + sqlite3_finalize(s->stmt_read_lock); + sqlite3_finalize(s->stmt_read_lock_by_uri); + sqlite3_finalize(s->stmt_create_lock); + sqlite3_finalize(s->stmt_remove_lock); + sqlite3_finalize(s->stmt_refresh_lock); sqlite3_close(s->sql); } -#endif +#endif free(s); } free(p->config_storage); @@ -135,16 +150,16 @@ FREE_FUNC(mod_webdav_free) { buffer_free(p->uri.path_raw); buffer_free(p->uri.path); buffer_free(p->uri.authority); - + buffer_free(p->physical.path); buffer_free(p->physical.rel_path); buffer_free(p->physical.doc_root); buffer_free(p->physical.basedir); - + buffer_free(p->tmp_buf); - + free(p); - + return HANDLER_GO_ON; } @@ -153,32 +168,32 @@ FREE_FUNC(mod_webdav_free) { SETDEFAULTS_FUNC(mod_webdav_set_defaults) { plugin_data *p = p_d; size_t i = 0; - - config_values_t cv[] = { + + config_values_t cv[] = { { "webdav.activate", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 0 */ { "webdav.is-readonly", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 1 */ { "webdav.sqlite-db-name", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 2 */ { "webdav.log-xml", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 3 */ { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET } }; - + if (!p) return HANDLER_ERROR; - + p->config_storage = calloc(1, srv->config_context->used * sizeof(specific_config *)); - + for (i = 0; i < srv->config_context->used; i++) { plugin_config *s; - + s = calloc(1, sizeof(plugin_config)); s->sqlite_db_name = buffer_init(); - + cv[0].destination = &(s->enabled); cv[1].destination = &(s->is_readonly); cv[2].destination = s->sqlite_db_name; cv[3].destination = &(s->log_xml); - + p->config_storage[i] = s; - + if (0 != config_insert_values_global(srv, ((data_config *)srv->config_context->data[i])->value, cv)) { return HANDLER_ERROR; } @@ -189,12 +204,32 @@ SETDEFAULTS_FUNC(mod_webdav_set_defaults) { char *err; if (SQLITE_OK != sqlite3_open(s->sqlite_db_name->ptr, &(s->sql))) { - log_error_write(srv, __FILE__, __LINE__, "s", "sqlite3_open failed"); + log_error_write(srv, __FILE__, __LINE__, "sbs", "sqlite3_open failed for", + s->sqlite_db_name, + sqlite3_errmsg(s->sql)); return HANDLER_ERROR; } - if (SQLITE_OK != sqlite3_prepare(s->sql, - CONST_STR_LEN("SELECT value FROM properties WHERE resource = ? AND prop = ? AND ns = ?"), + if (SQLITE_OK != sqlite3_exec(s->sql, + "CREATE TABLE properties (" + " resource TEXT NOT NULL," + " prop TEXT NOT NULL," + " ns TEXT NOT NULL," + " value TEXT NOT NULL," + " PRIMARY KEY(resource, prop, ns))", + NULL, NULL, &err)) { + + if (0 != strcmp(err, "table properties already exists")) { + log_error_write(srv, __FILE__, __LINE__, "ss", "can't open transaction:", err); + sqlite3_free(err); + + return HANDLER_ERROR; + } + sqlite3_free(err); + } + + if (SQLITE_OK != sqlite3_prepare(s->sql, + CONST_STR_LEN("SELECT value FROM properties WHERE resource = ? AND prop = ? AND ns = ?"), &(s->stmt_select_prop), &next_stmt)) { /* prepare failed */ @@ -202,8 +237,8 @@ SETDEFAULTS_FUNC(mod_webdav_set_defaults) { return HANDLER_ERROR; } - if (SQLITE_OK != sqlite3_prepare(s->sql, - CONST_STR_LEN("SELECT ns, prop FROM properties WHERE resource = ?"), + if (SQLITE_OK != sqlite3_prepare(s->sql, + CONST_STR_LEN("SELECT ns, prop FROM properties WHERE resource = ?"), &(s->stmt_select_propnames), &next_stmt)) { /* prepare failed */ @@ -211,16 +246,67 @@ SETDEFAULTS_FUNC(mod_webdav_set_defaults) { return HANDLER_ERROR; } - if (SQLITE_OK != sqlite3_exec(s->sql, - "CREATE TABLE properties (" + + if (SQLITE_OK != sqlite3_prepare(s->sql, + CONST_STR_LEN("REPLACE INTO properties (resource, prop, ns, value) VALUES (?, ?, ?, ?)"), + &(s->stmt_update_prop), &next_stmt)) { + /* prepare failed */ + + log_error_write(srv, __FILE__, __LINE__, "ss", "sqlite3_prepare failed:", sqlite3_errmsg(s->sql)); + return HANDLER_ERROR; + } + + if (SQLITE_OK != sqlite3_prepare(s->sql, + CONST_STR_LEN("DELETE FROM properties WHERE resource = ? AND prop = ? AND ns = ?"), + &(s->stmt_delete_prop), &next_stmt)) { + /* prepare failed */ + log_error_write(srv, __FILE__, __LINE__, "ss", "sqlite3_prepare failed", sqlite3_errmsg(s->sql)); + + return HANDLER_ERROR; + } + + if (SQLITE_OK != sqlite3_prepare(s->sql, + CONST_STR_LEN("DELETE FROM properties WHERE resource = ?"), + &(s->stmt_delete_uri), &next_stmt)) { + /* prepare failed */ + log_error_write(srv, __FILE__, __LINE__, "ss", "sqlite3_prepare failed", sqlite3_errmsg(s->sql)); + + return HANDLER_ERROR; + } + + if (SQLITE_OK != sqlite3_prepare(s->sql, + CONST_STR_LEN("INSERT INTO properties SELECT ?, prop, ns, value FROM properties WHERE resource = ?"), + &(s->stmt_copy_uri), &next_stmt)) { + /* prepare failed */ + log_error_write(srv, __FILE__, __LINE__, "ss", "sqlite3_prepare failed", sqlite3_errmsg(s->sql)); + + return HANDLER_ERROR; + } + + if (SQLITE_OK != sqlite3_prepare(s->sql, + CONST_STR_LEN("UPDATE properties SET resource = ? WHERE resource = ?"), + &(s->stmt_move_uri), &next_stmt)) { + /* prepare failed */ + log_error_write(srv, __FILE__, __LINE__, "ss", "sqlite3_prepare failed", sqlite3_errmsg(s->sql)); + + return HANDLER_ERROR; + } + + /* LOCKS */ + + if (SQLITE_OK != sqlite3_exec(s->sql, + "CREATE TABLE locks (" + " locktoken TEXT NOT NULL," " resource TEXT NOT NULL," - " prop TEXT NOT NULL," - " ns TEXT NOT NULL," - " value TEXT NOT NULL," - " PRIMARY KEY(resource, prop, ns))", + " lockscope TEXT NOT NULL," + " locktype TEXT NOT NULL," + " owner TEXT NOT NULL," + " depth INT NOT NULL," + " timeout TIMESTAMP NOT NULL," + " PRIMARY KEY(locktoken))", NULL, NULL, &err)) { - if (0 != strcmp(err, "table properties already exists")) { + if (0 != strcmp(err, "table locks already exists")) { log_error_write(srv, __FILE__, __LINE__, "ss", "can't open transaction:", err); sqlite3_free(err); @@ -228,127 +314,140 @@ SETDEFAULTS_FUNC(mod_webdav_set_defaults) { } sqlite3_free(err); } - - if (SQLITE_OK != sqlite3_prepare(s->sql, - CONST_STR_LEN("REPLACE INTO properties (resource, prop, ns, value) VALUES (?, ?, ?, ?)"), - &(s->stmt_update_prop), &next_stmt)) { + + if (SQLITE_OK != sqlite3_prepare(s->sql, + CONST_STR_LEN("INSERT INTO locks (locktoken, resource, lockscope, locktype, owner, depth, timeout) VALUES (?,?,?,?,?,?, CURRENT_TIME + 600)"), + &(s->stmt_create_lock), &next_stmt)) { /* prepare failed */ + log_error_write(srv, __FILE__, __LINE__, "ss", "sqlite3_prepare failed", sqlite3_errmsg(s->sql)); - log_error_write(srv, __FILE__, __LINE__, "ss", "sqlite3_prepare failed:", sqlite3_errmsg(s->sql)); return HANDLER_ERROR; } - if (SQLITE_OK != sqlite3_prepare(s->sql, - CONST_STR_LEN("DELETE FROM properties WHERE resource = ? AND prop = ? AND ns = ?"), - &(s->stmt_delete_prop), &next_stmt)) { + if (SQLITE_OK != sqlite3_prepare(s->sql, + CONST_STR_LEN("DELETE FROM locks WHERE locktoken = ?"), + &(s->stmt_remove_lock), &next_stmt)) { /* prepare failed */ log_error_write(srv, __FILE__, __LINE__, "ss", "sqlite3_prepare failed", sqlite3_errmsg(s->sql)); return HANDLER_ERROR; } - if (SQLITE_OK != sqlite3_prepare(s->sql, - CONST_STR_LEN("DELETE FROM properties WHERE resource = ?"), - &(s->stmt_delete_uri), &next_stmt)) { + if (SQLITE_OK != sqlite3_prepare(s->sql, + CONST_STR_LEN("SELECT locktoken, resource, lockscope, locktype, owner, depth, timeout FROM locks WHERE locktoken = ?"), + &(s->stmt_read_lock), &next_stmt)) { /* prepare failed */ log_error_write(srv, __FILE__, __LINE__, "ss", "sqlite3_prepare failed", sqlite3_errmsg(s->sql)); return HANDLER_ERROR; } - if (SQLITE_OK != sqlite3_prepare(s->sql, - CONST_STR_LEN("INSERT INTO properties SELECT ?, prop, ns, value FROM properties WHERE resource = ?"), - &(s->stmt_copy_uri), &next_stmt)) { + if (SQLITE_OK != sqlite3_prepare(s->sql, + CONST_STR_LEN("SELECT locktoken, resource, lockscope, locktype, owner, depth, timeout FROM locks WHERE resource = ?"), + &(s->stmt_read_lock_by_uri), &next_stmt)) { /* prepare failed */ log_error_write(srv, __FILE__, __LINE__, "ss", "sqlite3_prepare failed", sqlite3_errmsg(s->sql)); return HANDLER_ERROR; } - if (SQLITE_OK != sqlite3_prepare(s->sql, - CONST_STR_LEN("UPDATE properties SET resource = ? WHERE resource = ?"), - &(s->stmt_move_uri), &next_stmt)) { + if (SQLITE_OK != sqlite3_prepare(s->sql, + CONST_STR_LEN("UPDATE locks SET timeout = CURRENT_TIME + 600 WHERE locktoken = ?"), + &(s->stmt_refresh_lock), &next_stmt)) { /* prepare failed */ log_error_write(srv, __FILE__, __LINE__, "ss", "sqlite3_prepare failed", sqlite3_errmsg(s->sql)); return HANDLER_ERROR; } + + #else log_error_write(srv, __FILE__, __LINE__, "s", "Sorry, no sqlite3 and libxml2 support include, compile with --with-webdav-props"); return HANDLER_ERROR; #endif } } - + return HANDLER_GO_ON; } -#define PATCH(x) \ +#define PATCH_OPTION(x) \ p->conf.x = s->x; static int mod_webdav_patch_connection(server *srv, connection *con, plugin_data *p) { size_t i, j; plugin_config *s = p->config_storage[0]; - - PATCH(enabled); - PATCH(is_readonly); - PATCH(log_xml); - + + PATCH_OPTION(enabled); + PATCH_OPTION(is_readonly); + PATCH_OPTION(log_xml); + #ifdef USE_PROPPATCH - PATCH(sql); - PATCH(stmt_update_prop); - PATCH(stmt_delete_prop); - PATCH(stmt_select_prop); - PATCH(stmt_select_propnames); - - PATCH(stmt_delete_uri); - PATCH(stmt_move_uri); - PATCH(stmt_copy_uri); + PATCH_OPTION(sql); + PATCH_OPTION(stmt_update_prop); + PATCH_OPTION(stmt_delete_prop); + PATCH_OPTION(stmt_select_prop); + PATCH_OPTION(stmt_select_propnames); + + PATCH_OPTION(stmt_delete_uri); + PATCH_OPTION(stmt_move_uri); + PATCH_OPTION(stmt_copy_uri); + + PATCH_OPTION(stmt_remove_lock); + PATCH_OPTION(stmt_refresh_lock); + PATCH_OPTION(stmt_create_lock); + PATCH_OPTION(stmt_read_lock); + PATCH_OPTION(stmt_read_lock_by_uri); #endif /* skip the first, the global context */ for (i = 1; i < srv->config_context->used; i++) { data_config *dc = (data_config *)srv->config_context->data[i]; s = p->config_storage[i]; - + /* condition didn't match */ if (!config_check_cond(srv, con, dc)) continue; - + /* merge config */ for (j = 0; j < dc->value->used; j++) { data_unset *du = dc->value->data[j]; - + if (buffer_is_equal_string(du->key, CONST_STR_LEN("webdav.activate"))) { - PATCH(enabled); + PATCH_OPTION(enabled); } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("webdav.is-readonly"))) { - PATCH(is_readonly); + PATCH_OPTION(is_readonly); } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("webdav.log-xml"))) { - PATCH(log_xml); + PATCH_OPTION(log_xml); } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("webdav.sqlite-db-name"))) { #ifdef USE_PROPPATCH - PATCH(sql); - PATCH(stmt_update_prop); - PATCH(stmt_delete_prop); - PATCH(stmt_select_prop); - PATCH(stmt_select_propnames); - - PATCH(stmt_delete_uri); - PATCH(stmt_move_uri); - PATCH(stmt_copy_uri); + PATCH_OPTION(sql); + PATCH_OPTION(stmt_update_prop); + PATCH_OPTION(stmt_delete_prop); + PATCH_OPTION(stmt_select_prop); + PATCH_OPTION(stmt_select_propnames); + + PATCH_OPTION(stmt_delete_uri); + PATCH_OPTION(stmt_move_uri); + PATCH_OPTION(stmt_copy_uri); + + PATCH_OPTION(stmt_remove_lock); + PATCH_OPTION(stmt_refresh_lock); + PATCH_OPTION(stmt_create_lock); + PATCH_OPTION(stmt_read_lock); + PATCH_OPTION(stmt_read_lock_by_uri); #endif } } } - + return 0; } -#undef PATCH URIHANDLER_FUNC(mod_webdav_uri_handler) { plugin_data *p = p_d; - + UNUSED(srv); if (con->uri.path->used == 0) return HANDLER_GO_ON; - + mod_webdav_patch_connection(srv, con, p); if (!p->conf.enabled) return HANDLER_GO_ON; @@ -362,43 +461,43 @@ URIHANDLER_FUNC(mod_webdav_uri_handler) { if (p->conf.is_readonly) { response_header_insert(srv, con, CONST_STR_LEN("Allow"), CONST_STR_LEN("PROPFIND")); } else { - response_header_insert(srv, con, CONST_STR_LEN("Allow"), CONST_STR_LEN("PROPFIND, DELETE, MKCOL, PUT, MOVE, COPY, PROPPATCH")); + response_header_insert(srv, con, CONST_STR_LEN("Allow"), CONST_STR_LEN("PROPFIND, DELETE, MKCOL, PUT, MOVE, COPY, PROPPATCH, LOCK, UNLOCK")); } break; default: break; } - + /* not found */ return HANDLER_GO_ON; } -static int webdav_gen_prop_tag(server *srv, connection *con, - char *prop_name, - char *prop_ns, - char *value, +static int webdav_gen_prop_tag(server *srv, connection *con, + char *prop_name, + char *prop_ns, + char *value, buffer *b) { UNUSED(srv); UNUSED(con); if (value) { - buffer_append_string(b,"<"); + buffer_append_string_len(b,CONST_STR_LEN("<")); buffer_append_string(b, prop_name); - buffer_append_string(b, " xmlns=\""); + buffer_append_string_len(b, CONST_STR_LEN(" xmlns=\"")); buffer_append_string(b, prop_ns); - buffer_append_string(b, "\">"); + buffer_append_string_len(b, CONST_STR_LEN("\">")); buffer_append_string(b, value); - buffer_append_string(b,"</"); + buffer_append_string_len(b,CONST_STR_LEN("</")); buffer_append_string(b, prop_name); - buffer_append_string(b, ">"); + buffer_append_string_len(b, CONST_STR_LEN(">")); } else { - buffer_append_string(b,"<"); + buffer_append_string_len(b,CONST_STR_LEN("<")); buffer_append_string(b, prop_name); - buffer_append_string(b, " xmlns=\""); + buffer_append_string_len(b, CONST_STR_LEN(" xmlns=\"")); buffer_append_string(b, prop_ns); - buffer_append_string(b, "\"/>"); + buffer_append_string_len(b, CONST_STR_LEN("\"/>")); } return 0; @@ -408,24 +507,24 @@ static int webdav_gen_prop_tag(server *srv, connection *con, static int webdav_gen_response_status_tag(server *srv, connection *con, physical *dst, int status, buffer *b) { UNUSED(srv); - buffer_append_string(b,"<D:response xmlns:ns0=\"urn:uuid:c2f41010-65b3-11d1-a29f-00aa00c14882/\">\n"); + buffer_append_string_len(b,CONST_STR_LEN("<D:response xmlns:ns0=\"urn:uuid:c2f41010-65b3-11d1-a29f-00aa00c14882/\">\n")); - buffer_append_string(b,"<D:href>\n"); + buffer_append_string_len(b,CONST_STR_LEN("<D:href>\n")); buffer_append_string_buffer(b, dst->rel_path); - buffer_append_string(b,"</D:href>\n"); - buffer_append_string(b,"<D:status>\n"); - + buffer_append_string_len(b,CONST_STR_LEN("</D:href>\n")); + buffer_append_string_len(b,CONST_STR_LEN("<D:status>\n")); + if (con->request.http_version == HTTP_VERSION_1_1) { - BUFFER_COPY_STRING_CONST(b, "HTTP/1.1 "); + buffer_copy_string_len(b, CONST_STR_LEN("HTTP/1.1 ")); } else { - BUFFER_COPY_STRING_CONST(b, "HTTP/1.0 "); + buffer_copy_string_len(b, CONST_STR_LEN("HTTP/1.0 ")); } buffer_append_long(b, status); - BUFFER_APPEND_STRING_CONST(b, " "); + buffer_append_string_len(b, CONST_STR_LEN(" ")); buffer_append_string(b, get_http_status_name(status)); - buffer_append_string(b,"</D:status>\n"); - buffer_append_string(b,"</D:response>\n"); + buffer_append_string_len(b,CONST_STR_LEN("</D:status>\n")); + buffer_append_string_len(b,CONST_STR_LEN("</D:response>\n")); return 0; } @@ -458,16 +557,17 @@ static int webdav_delete_file(server *srv, connection *con, plugin_data *p, phys /* bind the values to the insert */ - sqlite3_bind_text(stmt, 1, - dst->rel_path->ptr, + sqlite3_bind_text(stmt, 1, + dst->rel_path->ptr, dst->rel_path->used - 1, SQLITE_TRANSIENT); - + if (SQLITE_DONE != sqlite3_step(stmt)) { /* */ - WP(); } } +#else + UNUSED(p); #endif } @@ -493,12 +593,12 @@ static int webdav_delete_dir(server *srv, connection *con, plugin_data *p, physi (de->d_name[0] == '.' && de->d_name[1] == '.' && de->d_name[2] == '\0')) { continue; /* ignore the parent dir */ - } + } buffer_copy_string_buffer(d.path, dst->path); BUFFER_APPEND_SLASH(d.path); buffer_append_string(d.path, de->d_name); - + buffer_copy_string_buffer(d.rel_path, dst->rel_path); BUFFER_APPEND_SLASH(d.rel_path); buffer_append_string(d.rel_path, de->d_name); @@ -508,7 +608,7 @@ static int webdav_delete_dir(server *srv, connection *con, plugin_data *p, physi /* don't about it yet, rmdir will fail too */ } else if (S_ISDIR(st.st_mode)) { have_multi_status = webdav_delete_dir(srv, con, p, &d, b); - + /* try to unlink it */ if (-1 == rmdir(d.path->ptr)) { switch(errno) { @@ -535,14 +635,13 @@ static int webdav_delete_dir(server *srv, connection *con, plugin_data *p, physi /* bind the values to the insert */ - sqlite3_bind_text(stmt, 1, - d.rel_path->ptr, + sqlite3_bind_text(stmt, 1, + d.rel_path->ptr, d.rel_path->used - 1, SQLITE_TRANSIENT); - + if (SQLITE_DONE != sqlite3_step(stmt)) { /* */ - WP(); } } #endif @@ -563,14 +662,14 @@ static int webdav_delete_dir(server *srv, connection *con, plugin_data *p, physi static int webdav_copy_file(server *srv, connection *con, plugin_data *p, physical *src, physical *dst, int overwrite) { stream s; int status = 0, ofd; - + UNUSED(srv); UNUSED(con); if (stream_open(&s, src->path)) { return 403; } - - if (-1 == (ofd = open(dst->path->ptr, O_WRONLY|O_TRUNC|O_CREAT|(overwrite ? 0 : O_EXCL), 0600))) { + + if (-1 == (ofd = open(dst->path->ptr, O_WRONLY|O_TRUNC|O_CREAT|(overwrite ? 0 : O_EXCL), WEBDAV_FILE_MODE))) { /* opening the destination failed for some reason */ switch(errno) { case EEXIST: @@ -601,7 +700,7 @@ static int webdav_copy_file(server *srv, connection *con, plugin_data *p, physic break; } } - + stream_close(&s); close(ofd); @@ -614,22 +713,23 @@ static int webdav_copy_file(server *srv, connection *con, plugin_data *p, physic sqlite3_reset(stmt); /* bind the values to the insert */ - sqlite3_bind_text(stmt, 1, - dst->rel_path->ptr, + sqlite3_bind_text(stmt, 1, + dst->rel_path->ptr, dst->rel_path->used - 1, SQLITE_TRANSIENT); - sqlite3_bind_text(stmt, 2, - src->rel_path->ptr, + sqlite3_bind_text(stmt, 2, + src->rel_path->ptr, src->rel_path->used - 1, SQLITE_TRANSIENT); - + if (SQLITE_DONE != sqlite3_step(stmt)) { /* */ - WP(); } } } +#else + UNUSED(p); #endif return status; } @@ -655,7 +755,7 @@ static int webdav_copy_dir(server *srv, connection *con, plugin_data *p, physica (de->d_name[0] == '.' && de->d_name[1] == '.' && de->d_name[2] == '\0')) { continue; } - + buffer_copy_string_buffer(s.path, src->path); BUFFER_APPEND_SLASH(s.path); buffer_append_string(s.path, de->d_name); @@ -676,7 +776,7 @@ static int webdav_copy_dir(server *srv, connection *con, plugin_data *p, physica /* why ? */ } else if (S_ISDIR(st.st_mode)) { /* a directory */ - if (-1 == mkdir(d.path->ptr, 0700) && + if (-1 == mkdir(d.path->ptr, WEBDAV_DIR_MODE) && errno != EEXIST) { /* WTH ? */ } else { @@ -692,19 +792,18 @@ static int webdav_copy_dir(server *srv, connection *con, plugin_data *p, physica sqlite3_reset(stmt); /* bind the values to the insert */ - sqlite3_bind_text(stmt, 1, - dst->rel_path->ptr, + sqlite3_bind_text(stmt, 1, + dst->rel_path->ptr, dst->rel_path->used - 1, SQLITE_TRANSIENT); - sqlite3_bind_text(stmt, 2, - src->rel_path->ptr, + sqlite3_bind_text(stmt, 2, + src->rel_path->ptr, src->rel_path->used - 1, SQLITE_TRANSIENT); - + if (SQLITE_DONE != sqlite3_step(stmt)) { /* */ - WP(); } } #endif @@ -721,7 +820,7 @@ static int webdav_copy_dir(server *srv, connection *con, plugin_data *p, physica buffer_free(s.rel_path); buffer_free(d.path); buffer_free(d.rel_path); - + closedir(srcdir); } @@ -741,23 +840,23 @@ static int webdav_get_live_property(server *srv, connection *con, plugin_data *p if (0 == strcmp(prop_name, "resourcetype")) { if (S_ISDIR(sce->st.st_mode)) { - buffer_append_string(b, "<D:resourcetype><D:collection/></D:resourcetype>"); + buffer_append_string_len(b, CONST_STR_LEN("<D:resourcetype><D:collection/></D:resourcetype>")); found = 1; } } else if (0 == strcmp(prop_name, "getcontenttype")) { if (S_ISDIR(sce->st.st_mode)) { - buffer_append_string(b, "<D:getcontenttype>httpd/unix-directory</D:getcontenttype>"); + buffer_append_string_len(b, CONST_STR_LEN("<D:getcontenttype>httpd/unix-directory</D:getcontenttype>")); found = 1; - } else if(S_ISREG(sce->st.st_mode)) { + } else if(S_ISREG(sce->st.st_mode)) { for (k = 0; k < con->conf.mimetypes->used; k++) { data_string *ds = (data_string *)con->conf.mimetypes->data[k]; - + if (ds->key->used == 0) continue; - + if (buffer_is_equal_right_len(dst->path, ds->key, ds->key->used - 1)) { - buffer_append_string(b,"<D:getcontenttype>"); + buffer_append_string_len(b,CONST_STR_LEN("<D:getcontenttype>")); buffer_append_string_buffer(b, ds->value); - buffer_append_string(b, "</D:getcontenttype>"); + buffer_append_string_len(b, CONST_STR_LEN("</D:getcontenttype>")); found = 1; break; @@ -765,26 +864,26 @@ static int webdav_get_live_property(server *srv, connection *con, plugin_data *p } } } else if (0 == strcmp(prop_name, "creationdate")) { - buffer_append_string(b, "<D:creationdate ns0:dt=\"dateTime.tz\">"); + buffer_append_string_len(b, CONST_STR_LEN("<D:creationdate ns0:dt=\"dateTime.tz\">")); strftime(ctime_buf, sizeof(ctime_buf), "%Y-%m-%dT%H:%M:%SZ", gmtime(&(sce->st.st_ctime))); buffer_append_string(b, ctime_buf); - buffer_append_string(b, "</D:creationdate>"); + buffer_append_string_len(b, CONST_STR_LEN("</D:creationdate>")); found = 1; } else if (0 == strcmp(prop_name, "getlastmodified")) { - buffer_append_string(b,"<D:getlastmodified ns0:dt=\"dateTime.rfc1123\">"); + buffer_append_string_len(b,CONST_STR_LEN("<D:getlastmodified ns0:dt=\"dateTime.rfc1123\">")); strftime(mtime_buf, sizeof(mtime_buf), "%a, %d %b %Y %H:%M:%S GMT", gmtime(&(sce->st.st_mtime))); buffer_append_string(b, mtime_buf); - buffer_append_string(b, "</D:getlastmodified>"); + buffer_append_string_len(b, CONST_STR_LEN("</D:getlastmodified>")); found = 1; } else if (0 == strcmp(prop_name, "getcontentlength")) { - buffer_append_string(b,"<D:getcontentlength>"); + buffer_append_string_len(b,CONST_STR_LEN("<D:getcontentlength>")); buffer_append_off_t(b, sce->st.st_size); - buffer_append_string(b, "</D:getcontentlength>"); + buffer_append_string_len(b, CONST_STR_LEN("</D:getcontentlength>")); found = 1; } else if (0 == strcmp(prop_name, "getcontentlanguage")) { - buffer_append_string(b,"<D:getcontentlanguage>"); - buffer_append_string(b, "en"); - buffer_append_string(b, "</D:getcontentlanguage>"); + buffer_append_string_len(b,CONST_STR_LEN("<D:getcontentlanguage>")); + buffer_append_string_len(b, CONST_STR_LEN("en")); + buffer_append_string_len(b, CONST_STR_LEN("</D:getcontentlanguage>")); found = 1; } } @@ -807,23 +906,23 @@ static int webdav_get_property(server *srv, connection *con, plugin_data *p, phy /* bind the values to the insert */ - sqlite3_bind_text(stmt, 1, - dst->rel_path->ptr, + sqlite3_bind_text(stmt, 1, + dst->rel_path->ptr, dst->rel_path->used - 1, SQLITE_TRANSIENT); - sqlite3_bind_text(stmt, 2, + sqlite3_bind_text(stmt, 2, prop_name, strlen(prop_name), SQLITE_TRANSIENT); - sqlite3_bind_text(stmt, 3, + sqlite3_bind_text(stmt, 3, prop_ns, strlen(prop_ns), SQLITE_TRANSIENT); /* it is the PK */ - while (SQLITE_ROW == sqlite3_step(p->conf.stmt_select_prop)) { + while (SQLITE_ROW == sqlite3_step(stmt)) { /* there is a row for us, we only expect a single col 'value' */ - webdav_gen_prop_tag(srv, con, prop_name, prop_ns, (char *)sqlite3_column_text(p->conf.stmt_select_prop, 0), b); + webdav_gen_prop_tag(srv, con, prop_name, prop_ns, (char *)sqlite3_column_text(stmt, 0), b); found = 1; } } @@ -840,7 +939,7 @@ typedef struct { char *prop; } webdav_property; -webdav_property live_properties[] = { +static webdav_property live_properties[] = { { "DAV:", "creationdate" }, { "DAV:", "displayname" }, { "DAV:", "getcontentlanguage" }, @@ -871,8 +970,8 @@ static int webdav_get_props(server *srv, connection *con, plugin_data *p, physic webdav_property *prop; prop = props->ptr[i]; - - if (0 != webdav_get_property(srv, con, p, + + if (0 != webdav_get_property(srv, con, p, dst, prop->prop, prop->ns, b_200)) { webdav_gen_prop_tag(srv, con, prop->prop, prop->ns, NULL, b_404); } @@ -916,13 +1015,15 @@ static int webdav_parse_chunkqueue(server *srv, connection *con, plugin_data *p, if (-1 == c->file.fd && /* open the file if not already open */ -1 == (c->file.fd = open(c->file.name->ptr, O_RDONLY))) { log_error_write(srv, __FILE__, __LINE__, "ss", "open failed: ", strerror(errno)); - + return -1; } - + if (MAP_FAILED == (c->file.mmap.start = mmap(0, c->file.length, PROT_READ, MAP_SHARED, c->file.fd, 0))) { - log_error_write(srv, __FILE__, __LINE__, "ssbd", "mmap failed: ", + log_error_write(srv, __FILE__, __LINE__, "ssbd", "mmap failed: ", strerror(errno), c->file.name, c->file.fd); + close(c->file.fd); + c->file.fd = -1; return -1; } @@ -936,9 +1037,9 @@ static int webdav_parse_chunkqueue(server *srv, connection *con, plugin_data *p, } if (XML_ERR_OK != (err = xmlParseChunk(ctxt, c->file.mmap.start + c->offset, weHave, 0))) { - log_error_write(srv, __FILE__, __LINE__, "sddd", "xmlParseChunk failed at:", cq->bytes_out, weHave, err); + log_error_write(srv, __FILE__, __LINE__, "sodd", "xmlParseChunk failed at:", cq->bytes_out, weHave, err); } - + c->offset += weHave; cq->bytes_out += weHave; @@ -954,9 +1055,9 @@ static int webdav_parse_chunkqueue(server *srv, connection *con, plugin_data *p, } if (XML_ERR_OK != (err = xmlParseChunk(ctxt, c->mem->ptr + c->offset, weHave, 0))) { - log_error_write(srv, __FILE__, __LINE__, "sddd", "xmlParseChunk failed at:", cq->bytes_out, weHave, err); + log_error_write(srv, __FILE__, __LINE__, "sodd", "xmlParseChunk failed at:", cq->bytes_out, weHave, err); } - + c->offset += weHave; cq->bytes_out += weHave; @@ -991,6 +1092,122 @@ static int webdav_parse_chunkqueue(server *srv, connection *con, plugin_data *p, } #endif +#ifdef USE_LOCKS +static int webdav_lockdiscovery(server *srv, connection *con, + buffer *locktoken, const char *lockscope, const char *locktype, int depth) { + + buffer *b; + + response_header_overwrite(srv, con, CONST_STR_LEN("Lock-Token"), CONST_BUF_LEN(locktoken)); + + response_header_overwrite(srv, con, + CONST_STR_LEN("Content-Type"), + CONST_STR_LEN("text/xml; charset=\"utf-8\"")); + + b = chunkqueue_get_append_buffer(con->write_queue); + + buffer_copy_string_len(b, CONST_STR_LEN("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n")); + + buffer_append_string_len(b,CONST_STR_LEN("<D:prop xmlns:D=\"DAV:\" xmlns:ns0=\"urn:uuid:c2f41010-65b3-11d1-a29f-00aa00c14882/\">\n")); + buffer_append_string_len(b,CONST_STR_LEN("<D:lockdiscovery>\n")); + buffer_append_string_len(b,CONST_STR_LEN("<D:activelock>\n")); + + buffer_append_string_len(b,CONST_STR_LEN("<D:lockscope>")); + buffer_append_string_len(b,CONST_STR_LEN("<D:")); + buffer_append_string(b, lockscope); + buffer_append_string_len(b, CONST_STR_LEN("/>")); + buffer_append_string_len(b,CONST_STR_LEN("</D:lockscope>\n")); + + buffer_append_string_len(b,CONST_STR_LEN("<D:locktype>")); + buffer_append_string_len(b,CONST_STR_LEN("<D:")); + buffer_append_string(b, locktype); + buffer_append_string_len(b, CONST_STR_LEN("/>")); + buffer_append_string_len(b,CONST_STR_LEN("</D:locktype>\n")); + + buffer_append_string_len(b,CONST_STR_LEN("<D:depth>")); + buffer_append_string(b, depth == 0 ? "0" : "infinity"); + buffer_append_string_len(b,CONST_STR_LEN("</D:depth>\n")); + + buffer_append_string_len(b,CONST_STR_LEN("<D:timeout>")); + buffer_append_string_len(b, CONST_STR_LEN("Second-600")); + buffer_append_string_len(b,CONST_STR_LEN("</D:timeout>\n")); + + buffer_append_string_len(b,CONST_STR_LEN("<D:owner>")); + buffer_append_string_len(b,CONST_STR_LEN("</D:owner>\n")); + + buffer_append_string_len(b,CONST_STR_LEN("<D:locktoken>")); + buffer_append_string_len(b, CONST_STR_LEN("<D:href>")); + buffer_append_string_buffer(b, locktoken); + buffer_append_string_len(b, CONST_STR_LEN("</D:href>")); + buffer_append_string_len(b,CONST_STR_LEN("</D:locktoken>\n")); + + buffer_append_string_len(b,CONST_STR_LEN("</D:activelock>\n")); + buffer_append_string_len(b,CONST_STR_LEN("</D:lockdiscovery>\n")); + buffer_append_string_len(b,CONST_STR_LEN("</D:prop>\n")); + + return 0; +} +#endif + +/** + * check if resource is having the right locks to access to resource + * + * + * + */ +static int webdav_has_lock(server *srv, connection *con, plugin_data *p, buffer *uri) { + int has_lock = 1; + +#ifdef USE_LOCKS + data_string *ds; + UNUSED(srv); + + /** + * This implementation is more fake than real + * we need a parser for the If: header to really handle the full scope + * + * X-Litmus: locks: 11 (owner_modify) + * If: <http://127.0.0.1:1025/dav/litmus/lockme> (<opaquelocktoken:2165478d-0611-49c4-be92-e790d68a38f1>) + * - a tagged check: + * if http://127.0.0.1:1025/dav/litmus/lockme is locked with + * opaquelocktoken:2165478d-0611-49c4-be92-e790d68a38f1, go on + * + * X-Litmus: locks: 16 (fail_cond_put) + * If: (<DAV:no-lock> ["-1622396671"]) + * - untagged: + * go on if the resource has the etag [...] and the lock + */ + if (NULL != (ds = (data_string *)array_get_element(con->request.headers, "If"))) { + /* Ooh, ooh. A if tag, now the fun begins. + * + * this can only work with a real parser + **/ + } else { + /* we didn't provided a lock-token -> */ + /* if the resource is locked -> 423 */ + + sqlite3_stmt *stmt = p->conf.stmt_read_lock_by_uri; + + sqlite3_reset(stmt); + + sqlite3_bind_text(stmt, 1, + CONST_BUF_LEN(uri), + SQLITE_TRANSIENT); + + while (SQLITE_ROW == sqlite3_step(stmt)) { + has_lock = 0; + } + } +#else + UNUSED(srv); + UNUSED(con); + UNUSED(p); + UNUSED(uri); +#endif + + return has_lock; +} + URIHANDLER_FUNC(mod_webdav_subrequest_handler) { plugin_data *p = p_d; buffer *b; @@ -1001,7 +1218,8 @@ URIHANDLER_FUNC(mod_webdav_subrequest_handler) { buffer *prop_200; buffer *prop_404; webdav_properties *req_props; - + stat_cache_entry *sce = NULL; + UNUSED(srv); if (!p->conf.enabled) return HANDLER_GO_ON; @@ -1019,7 +1237,19 @@ URIHANDLER_FUNC(mod_webdav_subrequest_handler) { req_props = NULL; /* is there a content-body ? */ - + + switch (stat_cache_get_entry(srv, con, con->physical.path, &sce)) { + case HANDLER_ERROR: + if (errno == ENOENT) { + con->http_status = 404; + return HANDLER_FINISHED; + } + break; + default: + break; + } + + #ifdef USE_PROPPATCH /* any special requests or just allprop ? */ if (con->request.content_length) { @@ -1087,14 +1317,13 @@ URIHANDLER_FUNC(mod_webdav_subrequest_handler) { /* get all property names (EMPTY) */ sqlite3_reset(stmt); /* bind the values to the insert */ - - sqlite3_bind_text(stmt, 1, - con->uri.path->ptr, + + sqlite3_bind_text(stmt, 1, + con->uri.path->ptr, con->uri.path->used - 1, SQLITE_TRANSIENT); - + if (SQLITE_DONE != sqlite3_step(stmt)) { - WP(); } } } else if (0 == xmlStrcmp(cmd->name, BAD_CAST "allprop")) { @@ -1115,13 +1344,13 @@ URIHANDLER_FUNC(mod_webdav_subrequest_handler) { response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_STR_LEN("text/xml; charset=\"utf-8\"")); b = chunkqueue_get_append_buffer(con->write_queue); - - buffer_copy_string(b, "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"); - buffer_append_string(b,"<D:multistatus xmlns:D=\"DAV:\" xmlns:ns0=\"urn:uuid:c2f41010-65b3-11d1-a29f-00aa00c14882/\">\n"); + buffer_copy_string_len(b, CONST_STR_LEN("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n")); + + buffer_append_string_len(b,CONST_STR_LEN("<D:multistatus xmlns:D=\"DAV:\" xmlns:ns0=\"urn:uuid:c2f41010-65b3-11d1-a29f-00aa00c14882/\">\n")); /* allprop */ - + prop_200 = buffer_init(); prop_404 = buffer_init(); @@ -1129,44 +1358,44 @@ URIHANDLER_FUNC(mod_webdav_subrequest_handler) { case 0: /* Depth: 0 */ webdav_get_props(srv, con, p, &(con->physical), req_props, prop_200, prop_404); - - buffer_append_string(b,"<D:response>\n"); - buffer_append_string(b,"<D:href>"); + + buffer_append_string_len(b,CONST_STR_LEN("<D:response>\n")); + buffer_append_string_len(b,CONST_STR_LEN("<D:href>")); buffer_append_string_buffer(b, con->uri.scheme); - buffer_append_string(b,"://"); + buffer_append_string_len(b,CONST_STR_LEN("://")); buffer_append_string_buffer(b, con->uri.authority); buffer_append_string_encoded(b, CONST_BUF_LEN(con->uri.path), ENCODING_REL_URI); - buffer_append_string(b,"</D:href>\n"); + buffer_append_string_len(b,CONST_STR_LEN("</D:href>\n")); if (!buffer_is_empty(prop_200)) { - buffer_append_string(b,"<D:propstat>\n"); - buffer_append_string(b,"<D:prop>\n"); + buffer_append_string_len(b,CONST_STR_LEN("<D:propstat>\n")); + buffer_append_string_len(b,CONST_STR_LEN("<D:prop>\n")); buffer_append_string_buffer(b, prop_200); - buffer_append_string(b,"</D:prop>\n"); - - buffer_append_string(b,"<D:status>HTTP/1.1 200 OK</D:status>\n"); - - buffer_append_string(b,"</D:propstat>\n"); + buffer_append_string_len(b,CONST_STR_LEN("</D:prop>\n")); + + buffer_append_string_len(b,CONST_STR_LEN("<D:status>HTTP/1.1 200 OK</D:status>\n")); + + buffer_append_string_len(b,CONST_STR_LEN("</D:propstat>\n")); } if (!buffer_is_empty(prop_404)) { - buffer_append_string(b,"<D:propstat>\n"); - buffer_append_string(b,"<D:prop>\n"); + buffer_append_string_len(b,CONST_STR_LEN("<D:propstat>\n")); + buffer_append_string_len(b,CONST_STR_LEN("<D:prop>\n")); buffer_append_string_buffer(b, prop_404); - buffer_append_string(b,"</D:prop>\n"); - - buffer_append_string(b,"<D:status>HTTP/1.1 404 Not Found</D:status>\n"); - - buffer_append_string(b,"</D:propstat>\n"); + buffer_append_string_len(b,CONST_STR_LEN("</D:prop>\n")); + + buffer_append_string_len(b,CONST_STR_LEN("<D:status>HTTP/1.1 404 Not Found</D:status>\n")); + + buffer_append_string_len(b,CONST_STR_LEN("</D:propstat>\n")); } - buffer_append_string(b,"</D:response>\n"); + buffer_append_string_len(b,CONST_STR_LEN("</D:response>\n")); break; - case 1: + case 1: if (NULL != (dir = opendir(con->physical.path->ptr))) { struct dirent *de; physical d; @@ -1179,7 +1408,7 @@ URIHANDLER_FUNC(mod_webdav_subrequest_handler) { if (de->d_name[0] == '.' && de->d_name[1] == '.' && de->d_name[2] == '\0') { continue; /* ignore the parent dir */ - } + } buffer_copy_string_buffer(d.path, dst->path); BUFFER_APPEND_SLASH(d.path); @@ -1188,7 +1417,7 @@ URIHANDLER_FUNC(mod_webdav_subrequest_handler) { BUFFER_APPEND_SLASH(d.rel_path); if (de->d_name[0] == '.' && de->d_name[1] == '\0') { - /* don't append the . */ + /* don't append the . */ } else { buffer_append_string(d.path, de->d_name); buffer_append_string(d.rel_path, de->d_name); @@ -1198,41 +1427,41 @@ URIHANDLER_FUNC(mod_webdav_subrequest_handler) { buffer_reset(prop_404); webdav_get_props(srv, con, p, &d, req_props, prop_200, prop_404); - - buffer_append_string(b,"<D:response>\n"); - buffer_append_string(b,"<D:href>"); + + buffer_append_string_len(b,CONST_STR_LEN("<D:response>\n")); + buffer_append_string_len(b,CONST_STR_LEN("<D:href>")); buffer_append_string_buffer(b, con->uri.scheme); - buffer_append_string(b,"://"); + buffer_append_string_len(b,CONST_STR_LEN("://")); buffer_append_string_buffer(b, con->uri.authority); buffer_append_string_encoded(b, CONST_BUF_LEN(d.rel_path), ENCODING_REL_URI); - buffer_append_string(b,"</D:href>\n"); + buffer_append_string_len(b,CONST_STR_LEN("</D:href>\n")); if (!buffer_is_empty(prop_200)) { - buffer_append_string(b,"<D:propstat>\n"); - buffer_append_string(b,"<D:prop>\n"); + buffer_append_string_len(b,CONST_STR_LEN("<D:propstat>\n")); + buffer_append_string_len(b,CONST_STR_LEN("<D:prop>\n")); buffer_append_string_buffer(b, prop_200); - buffer_append_string(b,"</D:prop>\n"); - - buffer_append_string(b,"<D:status>HTTP/1.1 200 OK</D:status>\n"); - - buffer_append_string(b,"</D:propstat>\n"); + buffer_append_string_len(b,CONST_STR_LEN("</D:prop>\n")); + + buffer_append_string_len(b,CONST_STR_LEN("<D:status>HTTP/1.1 200 OK</D:status>\n")); + + buffer_append_string_len(b,CONST_STR_LEN("</D:propstat>\n")); } if (!buffer_is_empty(prop_404)) { - buffer_append_string(b,"<D:propstat>\n"); - buffer_append_string(b,"<D:prop>\n"); + buffer_append_string_len(b,CONST_STR_LEN("<D:propstat>\n")); + buffer_append_string_len(b,CONST_STR_LEN("<D:prop>\n")); buffer_append_string_buffer(b, prop_404); - buffer_append_string(b,"</D:prop>\n"); - - buffer_append_string(b,"<D:status>HTTP/1.1 404 Not Found</D:status>\n"); - - buffer_append_string(b,"</D:propstat>\n"); + buffer_append_string_len(b,CONST_STR_LEN("</D:prop>\n")); + + buffer_append_string_len(b,CONST_STR_LEN("<D:status>HTTP/1.1 404 Not Found</D:status>\n")); + + buffer_append_string_len(b,CONST_STR_LEN("</D:propstat>\n")); } - buffer_append_string(b,"</D:response>\n"); + buffer_append_string_len(b,CONST_STR_LEN("</D:response>\n")); } closedir(dir); buffer_free(d.path); @@ -1255,7 +1484,7 @@ URIHANDLER_FUNC(mod_webdav_subrequest_handler) { buffer_free(prop_200); buffer_free(prop_404); - buffer_append_string(b,"</D:multistatus>\n"); + buffer_append_string_len(b,CONST_STR_LEN("</D:multistatus>\n")); if (p->conf.log_xml) { log_error_write(srv, __FILE__, __LINE__, "sb", "XML-response-body:", b); @@ -1275,10 +1504,10 @@ URIHANDLER_FUNC(mod_webdav_subrequest_handler) { return HANDLER_FINISHED; } - + /* let's create the directory */ - if (-1 == mkdir(con->physical.path->ptr, 0700)) { + if (-1 == mkdir(con->physical.path->ptr, WEBDAV_DIR_MODE)) { switch(errno) { case EPERM: con->http_status = 403; @@ -1294,6 +1523,7 @@ URIHANDLER_FUNC(mod_webdav_subrequest_handler) { } } else { con->http_status = 201; + con->file_finished = 1; } return HANDLER_FINISHED; @@ -1302,7 +1532,13 @@ URIHANDLER_FUNC(mod_webdav_subrequest_handler) { con->http_status = 403; return HANDLER_FINISHED; } - + + /* does the client have a lock for this connection ? */ + if (!webdav_has_lock(srv, con, p, con->uri.path)) { + con->http_status = 423; + return HANDLER_FINISHED; + } + /* stat and unlink afterwards */ if (-1 == stat(con->physical.path->ptr, &st)) { /* don't about it yet, unlink will fail too */ @@ -1322,15 +1558,15 @@ URIHANDLER_FUNC(mod_webdav_subrequest_handler) { response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_STR_LEN("text/xml; charset=\"utf-8\"")); b = chunkqueue_get_append_buffer(con->write_queue); - - buffer_copy_string(b, "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"); - buffer_append_string(b,"<D:multistatus xmlns:D=\"DAV:\">\n"); + buffer_copy_string_len(b, CONST_STR_LEN("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n")); + + buffer_append_string_len(b,CONST_STR_LEN("<D:multistatus xmlns:D=\"DAV:\">\n")); buffer_append_string_buffer(b, multi_status_resp); - buffer_append_string(b,"</D:multistatus>\n"); - + buffer_append_string_len(b,CONST_STR_LEN("</D:multistatus>\n")); + if (p->conf.log_xml) { log_error_write(srv, __FILE__, __LINE__, "sb", "XML-response-body:", b); } @@ -1339,7 +1575,7 @@ URIHANDLER_FUNC(mod_webdav_subrequest_handler) { con->file_finished = 1; } else { /* everything went fine, remove the directory */ - + if (-1 == rmdir(con->physical.path->ptr)) { switch(errno) { case ENOENT: @@ -1374,106 +1610,194 @@ URIHANDLER_FUNC(mod_webdav_subrequest_handler) { case HTTP_METHOD_PUT: { int fd; chunkqueue *cq = con->request_content_queue; + chunk *c; + data_string *ds_range; if (p->conf.is_readonly) { con->http_status = 403; return HANDLER_FINISHED; } + /* is a exclusive lock set on the source */ + if (!webdav_has_lock(srv, con, p, con->uri.path)) { + con->http_status = 423; + return HANDLER_FINISHED; + } + + assert(chunkqueue_length(cq) == (off_t)con->request.content_length); - /* taken what we have in the request-body and write it to a file */ - if (-1 == (fd = open(con->physical.path->ptr, O_WRONLY|O_CREAT|O_TRUNC, 0600))) { - /* we can't open the file */ - con->http_status = 403; + /* RFC2616 Section 9.6 PUT requires us to send 501 on all Content-* we don't support + * - most important Content-Range + * + * + * Example: Content-Range: bytes 100-1037/1038 */ + + if (NULL != (ds_range = (data_string *)array_get_element(con->request.headers, "Content-Range"))) { + const char *num = ds_range->value->ptr; + off_t offset; + char *err = NULL; + + if (0 != strncmp(num, "bytes ", 6)) { + con->http_status = 501; /* not implemented */ + + return HANDLER_FINISHED; + } + + /* we only support <num>- ... */ + + num += 6; + + /* skip WS */ + while (*num == ' ' || *num == '\t') num++; + + if (*num == '\0') { + con->http_status = 501; /* not implemented */ + + return HANDLER_FINISHED; + } + + offset = strtoll(num, &err, 10); + + if (*err != '-' || offset < 0) { + con->http_status = 501; /* not implemented */ + + return HANDLER_FINISHED; + } + + if (-1 == (fd = open(con->physical.path->ptr, O_WRONLY, WEBDAV_FILE_MODE))) { + switch (errno) { + case ENOENT: + con->http_status = 404; /* not found */ + break; + default: + con->http_status = 403; /* not found */ + break; + } + return HANDLER_FINISHED; + } + + if (-1 == lseek(fd, offset, SEEK_SET)) { + con->http_status = 501; /* not implemented */ + + close(fd); + + return HANDLER_FINISHED; + } + con->http_status = 200; /* modified */ } else { - chunk *c; + /* take what we have in the request-body and write it to a file */ - con->http_status = 201; /* created */ + /* if the file doesn't exist, create it */ + if (-1 == (fd = open(con->physical.path->ptr, O_WRONLY|O_TRUNC, WEBDAV_FILE_MODE))) { + if (errno == ENOENT && + -1 == (fd = open(con->physical.path->ptr, O_WRONLY|O_CREAT|O_TRUNC|O_EXCL, WEBDAV_FILE_MODE))) { + /* we can't open the file */ + con->http_status = 403; - for (c = cq->first; c; c = cq->first) { - int r = 0; + return HANDLER_FINISHED; + } else { + con->http_status = 201; /* created */ + } + } else { + con->http_status = 200; /* modified */ + } + } - /* copy all chunks */ - switch(c->type) { - case FILE_CHUNK: + con->file_finished = 1; - if (c->file.mmap.start == MAP_FAILED) { - if (-1 == c->file.fd && /* open the file if not already open */ - -1 == (c->file.fd = open(c->file.name->ptr, O_RDONLY))) { - log_error_write(srv, __FILE__, __LINE__, "ss", "open failed: ", strerror(errno)); - - return -1; - } - - if (MAP_FAILED == (c->file.mmap.start = mmap(0, c->file.length, PROT_READ, MAP_SHARED, c->file.fd, 0))) { - log_error_write(srv, __FILE__, __LINE__, "ssbd", "mmap failed: ", - strerror(errno), c->file.name, c->file.fd); + for (c = cq->first; c; c = cq->first) { + int r = 0; - return -1; - } + /* copy all chunks */ + switch(c->type) { + case FILE_CHUNK: - c->file.mmap.length = c->file.length; + if (c->file.mmap.start == MAP_FAILED) { + if (-1 == c->file.fd && /* open the file if not already open */ + -1 == (c->file.fd = open(c->file.name->ptr, O_RDONLY))) { + log_error_write(srv, __FILE__, __LINE__, "ss", "open failed: ", strerror(errno)); + return HANDLER_ERROR; + } + + if (MAP_FAILED == (c->file.mmap.start = mmap(NULL, c->file.length, PROT_READ, MAP_SHARED, c->file.fd, 0))) { + log_error_write(srv, __FILE__, __LINE__, "ssbd", "mmap failed: ", + strerror(errno), c->file.name, c->file.fd); close(c->file.fd); c->file.fd = -1; - - /* chunk_reset() or chunk_free() will cleanup for us */ - } - if ((r = write(fd, c->file.mmap.start + c->offset, c->file.length - c->offset)) < 0) { - switch(errno) { - case ENOSPC: - con->http_status = 507; - - break; - default: - con->http_status = 403; - break; - } + return HANDLER_ERROR; } - break; - case MEM_CHUNK: - if ((r = write(fd, c->mem->ptr + c->offset, c->mem->used - c->offset - 1)) < 0) { - switch(errno) { - case ENOSPC: - con->http_status = 507; - - break; - default: - con->http_status = 403; - break; - } + + c->file.mmap.length = c->file.length; + + close(c->file.fd); + c->file.fd = -1; + + /* chunk_reset() or chunk_free() will cleanup for us */ + } + + if ((r = write(fd, c->file.mmap.start + c->offset, c->file.length - c->offset)) < 0) { + switch(errno) { + case ENOSPC: + con->http_status = 507; + + break; + default: + con->http_status = 403; + break; } - break; - case UNUSED_CHUNK: - break; } + break; + case MEM_CHUNK: + if ((r = write(fd, c->mem->ptr + c->offset, c->mem->used - c->offset - 1)) < 0) { + switch(errno) { + case ENOSPC: + con->http_status = 507; - if (r > 0) { - c->offset += r; - cq->bytes_out += r; - } else { - break; + break; + default: + con->http_status = 403; + break; + } } - chunkqueue_remove_finished_chunks(cq); + break; + case UNUSED_CHUNK: + break; } - close(fd); + if (r > 0) { + c->offset += r; + cq->bytes_out += r; + } else { + break; + } + chunkqueue_remove_finished_chunks(cq); } + close(fd); + return HANDLER_FINISHED; } - case HTTP_METHOD_MOVE: + case HTTP_METHOD_MOVE: case HTTP_METHOD_COPY: { buffer *destination = NULL; - char *sep, *start; + char *sep, *sep2, *start; int overwrite = 1; if (p->conf.is_readonly) { con->http_status = 403; return HANDLER_FINISHED; } - + + /* is a exclusive lock set on the source */ + if (con->request.http_method == HTTP_METHOD_MOVE) { + if (!webdav_has_lock(srv, con, p, con->uri.path)) { + con->http_status = 423; + return HANDLER_FINISHED; + } + } + if (NULL != (ds = (data_string *)array_get_element(con->request.headers, "Destination"))) { destination = ds->value; } else { @@ -1517,6 +1841,10 @@ URIHANDLER_FUNC(mod_webdav_subrequest_handler) { con->http_status = 400; return HANDLER_FINISHED; } + if (NULL != (sep2 = memchr(start, '@', sep - start))) { + /* skip login information */ + start = sep2 + 1; + } buffer_copy_string_len(p->uri.authority, start, sep - start); start = sep + 1; @@ -1550,7 +1878,7 @@ URIHANDLER_FUNC(mod_webdav_subrequest_handler) { BUFFER_APPEND_SLASH(p->physical.path); buffer_copy_string_buffer(p->physical.basedir, p->physical.path); - /* don't add a second / */ + /* don't add a second / */ if (p->physical.rel_path->ptr[0] == '/') { buffer_append_string_len(p->physical.path, p->physical.rel_path->ptr + 1, p->physical.rel_path->used - 2); } else { @@ -1575,7 +1903,7 @@ URIHANDLER_FUNC(mod_webdav_subrequest_handler) { /* src is a directory */ if (-1 == stat(p->physical.path->ptr, &st)) { - if (-1 == mkdir(p->physical.path->ptr, 0700)) { + if (-1 == mkdir(p->physical.path->ptr, WEBDAV_DIR_MODE)) { con->http_status = 403; return HANDLER_FINISHED; } @@ -1586,7 +1914,7 @@ URIHANDLER_FUNC(mod_webdav_subrequest_handler) { return HANDLER_FINISHED; } else { unlink(p->physical.path->ptr); - if (-1 == mkdir(p->physical.path->ptr, 0700)) { + if (-1 == mkdir(p->physical.path->ptr, WEBDAV_DIR_MODE)) { con->http_status = 403; return HANDLER_FINISHED; } @@ -1606,10 +1934,17 @@ URIHANDLER_FUNC(mod_webdav_subrequest_handler) { rmdir(con->physical.path->ptr); } con->http_status = 201; + con->file_finished = 1; } else { /* it is just a file, good */ int r; + /* does the client have a lock for this connection ? */ + if (!webdav_has_lock(srv, con, p, p->uri.path)) { + con->http_status = 423; + return HANDLER_FINISHED; + } + /* destination exists */ if (0 == (r = stat(p->physical.path->ptr, &st))) { if (S_ISDIR(st.st_mode)) { @@ -1625,6 +1960,7 @@ URIHANDLER_FUNC(mod_webdav_subrequest_handler) { if (-1 == r) { con->http_status = 201; /* we will create a new one */ + con->file_finished = 1; switch(errno) { case ENOTDIR: @@ -1632,7 +1968,7 @@ URIHANDLER_FUNC(mod_webdav_subrequest_handler) { return HANDLER_FINISHED; } } else if (overwrite == 0) { - /* destination exists, but overwrite is not set */ + /* destination exists, but overwrite is not set */ con->http_status = 412; return HANDLER_FINISHED; } else { @@ -1644,23 +1980,40 @@ URIHANDLER_FUNC(mod_webdav_subrequest_handler) { if (0 == rename(con->physical.path->ptr, p->physical.path->ptr)) { #ifdef USE_PROPPATCH - sqlite3_stmt *stmt = p->conf.stmt_move_uri; + sqlite3_stmt *stmt; + stmt = p->conf.stmt_delete_uri; if (stmt) { sqlite3_reset(stmt); /* bind the values to the insert */ - sqlite3_bind_text(stmt, 1, - p->uri.path->ptr, + sqlite3_bind_text(stmt, 1, + con->uri.path->ptr, + con->uri.path->used - 1, + SQLITE_TRANSIENT); + + if (SQLITE_DONE != sqlite3_step(stmt)) { + log_error_write(srv, __FILE__, __LINE__, "ss", "sql-move(delete old) failed:", sqlite3_errmsg(p->conf.sql)); + } + } + + stmt = p->conf.stmt_move_uri; + if (stmt) { + + sqlite3_reset(stmt); + + /* bind the values to the insert */ + sqlite3_bind_text(stmt, 1, + p->uri.path->ptr, p->uri.path->used - 1, SQLITE_TRANSIENT); - sqlite3_bind_text(stmt, 2, - con->uri.path->ptr, + sqlite3_bind_text(stmt, 2, + con->uri.path->ptr, con->uri.path->used - 1, SQLITE_TRANSIENT); - + if (SQLITE_DONE != sqlite3_step(stmt)) { log_error_write(srv, __FILE__, __LINE__, "ss", "sql-move failed:", sqlite3_errmsg(p->conf.sql)); } @@ -1687,12 +2040,17 @@ URIHANDLER_FUNC(mod_webdav_subrequest_handler) { return HANDLER_FINISHED; } - case HTTP_METHOD_PROPPATCH: { + case HTTP_METHOD_PROPPATCH: if (p->conf.is_readonly) { con->http_status = 403; return HANDLER_FINISHED; } + if (!webdav_has_lock(srv, con, p, con->uri.path)) { + con->http_status = 423; + return HANDLER_FINISHED; + } + /* check if destination exists */ if (-1 == stat(con->physical.path->ptr, &st)) { switch(errno) { @@ -1733,7 +2091,7 @@ URIHANDLER_FUNC(mod_webdav_subrequest_handler) { sqlite3_stmt *stmt; - stmt = (0 == xmlStrcmp(cmd->name, BAD_CAST "remove")) ? + stmt = (0 == xmlStrcmp(cmd->name, BAD_CAST "remove")) ? p->conf.stmt_delete_prop : p->conf.stmt_update_prop; for (props = cmd->children; props; props = props->next) { @@ -1758,34 +2116,35 @@ URIHANDLER_FUNC(mod_webdav_subrequest_handler) { /* bind the values to the insert */ - sqlite3_bind_text(stmt, 1, - con->uri.path->ptr, + sqlite3_bind_text(stmt, 1, + con->uri.path->ptr, con->uri.path->used - 1, SQLITE_TRANSIENT); - sqlite3_bind_text(stmt, 2, + sqlite3_bind_text(stmt, 2, (char *)prop->name, strlen((char *)prop->name), SQLITE_TRANSIENT); if (prop->ns) { - sqlite3_bind_text(stmt, 3, + sqlite3_bind_text(stmt, 3, (char *)prop->ns->href, strlen((char *)prop->ns->href), SQLITE_TRANSIENT); } else { - sqlite3_bind_text(stmt, 3, + sqlite3_bind_text(stmt, 3, "", 0, SQLITE_TRANSIENT); } if (stmt == p->conf.stmt_update_prop) { - sqlite3_bind_text(stmt, 4, + sqlite3_bind_text(stmt, 4, (char *)xmlNodeGetContent(prop), strlen((char *)xmlNodeGetContent(prop)), SQLITE_TRANSIENT); } - + if (SQLITE_DONE != (r = sqlite3_step(stmt))) { - log_error_write(srv, __FILE__, __LINE__, "ss", "sql-set failed:", sqlite3_errmsg(p->conf.sql)); + log_error_write(srv, __FILE__, __LINE__, "ss", + "sql-set failed:", sqlite3_errmsg(p->conf.sql)); } } } @@ -1800,7 +2159,7 @@ URIHANDLER_FUNC(mod_webdav_subrequest_handler) { goto propmatch_cleanup; } - + con->http_status = 400; } else { if (SQLITE_OK != sqlite3_exec(p->conf.sql, "COMMIT", NULL, NULL, &err)) { @@ -1817,6 +2176,7 @@ URIHANDLER_FUNC(mod_webdav_subrequest_handler) { } propmatch_cleanup: + xmlFreeDoc(xml); } else { con->http_status = 400; @@ -1826,11 +2186,307 @@ propmatch_cleanup: #endif con->http_status = 501; return HANDLER_FINISHED; - } + case HTTP_METHOD_LOCK: + /** + * a mac wants to write + * + * LOCK /dav/expire.txt HTTP/1.1\r\n + * User-Agent: WebDAVFS/1.3 (01308000) Darwin/8.1.0 (Power Macintosh)\r\n + * Accept: * / *\r\n + * Depth: 0\r\n + * Timeout: Second-600\r\n + * Content-Type: text/xml; charset=\"utf-8\"\r\n + * Content-Length: 229\r\n + * Connection: keep-alive\r\n + * Host: 192.168.178.23:1025\r\n + * \r\n + * <?xml version=\"1.0\" encoding=\"utf-8\"?>\n + * <D:lockinfo xmlns:D=\"DAV:\">\n + * <D:lockscope><D:exclusive/></D:lockscope>\n + * <D:locktype><D:write/></D:locktype>\n + * <D:owner>\n + * <D:href>http://www.apple.com/webdav_fs/</D:href>\n + * </D:owner>\n + * </D:lockinfo>\n + */ + + if (depth != 0 && depth != -1) { + con->http_status = 400; + + return HANDLER_FINISHED; + } + +#ifdef USE_LOCKS + if (con->request.content_length) { + xmlDocPtr xml; + buffer *hdr_if = NULL; + + if (NULL != (ds = (data_string *)array_get_element(con->request.headers, "If"))) { + hdr_if = ds->value; + } + + /* we don't support Depth: Infinity on locks */ + if (hdr_if == NULL && depth == -1) { + con->http_status = 409; /* Conflict */ + + return HANDLER_FINISHED; + } + + if (1 == webdav_parse_chunkqueue(srv, con, p, con->request_content_queue, &xml)) { + xmlNode *rootnode = xmlDocGetRootElement(xml); + + assert(rootnode); + + if (0 == xmlStrcmp(rootnode->name, BAD_CAST "lockinfo")) { + xmlNode *lockinfo; + const xmlChar *lockscope = NULL, *locktype = NULL; /* TODO: compiler says unused: *owner = NULL; */ + + for (lockinfo = rootnode->children; lockinfo; lockinfo = lockinfo->next) { + if (0 == xmlStrcmp(lockinfo->name, BAD_CAST "lockscope")) { + xmlNode *value; + for (value = lockinfo->children; value; value = value->next) { + if ((0 == xmlStrcmp(value->name, BAD_CAST "exclusive")) || + (0 == xmlStrcmp(value->name, BAD_CAST "shared"))) { + lockscope = value->name; + } else { + con->http_status = 400; + + xmlFreeDoc(xml); + return HANDLER_FINISHED; + } + } + } else if (0 == xmlStrcmp(lockinfo->name, BAD_CAST "locktype")) { + xmlNode *value; + for (value = lockinfo->children; value; value = value->next) { + if ((0 == xmlStrcmp(value->name, BAD_CAST "write"))) { + locktype = value->name; + } else { + con->http_status = 400; + + xmlFreeDoc(xml); + return HANDLER_FINISHED; + } + } + + } else if (0 == xmlStrcmp(lockinfo->name, BAD_CAST "owner")) { + } + } + + if (lockscope && locktype) { + sqlite3_stmt *stmt = p->conf.stmt_read_lock_by_uri; + + /* is this resourse already locked ? */ + + /* SELECT locktoken, resource, lockscope, locktype, owner, depth, timeout + * FROM locks + * WHERE resource = ? */ + + if (stmt) { + + sqlite3_reset(stmt); + + sqlite3_bind_text(stmt, 1, + p->uri.path->ptr, + p->uri.path->used - 1, + SQLITE_TRANSIENT); + + /* it is the PK */ + while (SQLITE_ROW == sqlite3_step(stmt)) { + /* we found a lock + * 1. is it compatible ? + * 2. is it ours */ + char *sql_lockscope = (char *)sqlite3_column_text(stmt, 2); + + if (strcmp(sql_lockscope, "exclusive")) { + con->http_status = 423; + } else if (0 == xmlStrcmp(lockscope, BAD_CAST "exclusive")) { + /* resourse is locked with a shared lock + * client wants exclusive */ + con->http_status = 423; + } + } + if (con->http_status == 423) { + xmlFreeDoc(xml); + return HANDLER_FINISHED; + } + } + + stmt = p->conf.stmt_create_lock; + if (stmt) { + /* create a lock-token */ + uuid_t id; + char uuid[37] /* 36 + \0 */; + + uuid_generate(id); + uuid_unparse(id, uuid); + + buffer_copy_string_len(p->tmp_buf, CONST_STR_LEN("opaquelocktoken:")); + buffer_append_string(p->tmp_buf, uuid); + + /* "CREATE TABLE locks (" + * " locktoken TEXT NOT NULL," + * " resource TEXT NOT NULL," + * " lockscope TEXT NOT NULL," + * " locktype TEXT NOT NULL," + * " owner TEXT NOT NULL," + * " depth INT NOT NULL," + */ + + sqlite3_reset(stmt); + + sqlite3_bind_text(stmt, 1, + CONST_BUF_LEN(p->tmp_buf), + SQLITE_TRANSIENT); + + sqlite3_bind_text(stmt, 2, + CONST_BUF_LEN(con->uri.path), + SQLITE_TRANSIENT); + + sqlite3_bind_text(stmt, 3, + (const char *)lockscope, + xmlStrlen(lockscope), + SQLITE_TRANSIENT); + + sqlite3_bind_text(stmt, 4, + (const char *)locktype, + xmlStrlen(locktype), + SQLITE_TRANSIENT); + + /* owner */ + sqlite3_bind_text(stmt, 5, + "", + 0, + SQLITE_TRANSIENT); + + /* depth */ + sqlite3_bind_int(stmt, 6, + depth); + + + if (SQLITE_DONE != sqlite3_step(stmt)) { + log_error_write(srv, __FILE__, __LINE__, "ss", + "create lock:", sqlite3_errmsg(p->conf.sql)); + } + + /* looks like we survived */ + webdav_lockdiscovery(srv, con, p->tmp_buf, (const char *)lockscope, (const char *)locktype, depth); + + con->http_status = 201; + con->file_finished = 1; + } + } + } + + xmlFreeDoc(xml); + return HANDLER_FINISHED; + } else { + con->http_status = 400; + return HANDLER_FINISHED; + } + } else { + + if (NULL != (ds = (data_string *)array_get_element(con->request.headers, "If"))) { + buffer *locktoken = ds->value; + sqlite3_stmt *stmt = p->conf.stmt_refresh_lock; + + /* remove the < > around the token */ + if (locktoken->used < 6) { + con->http_status = 400; + + return HANDLER_FINISHED; + } + + buffer_copy_string_len(p->tmp_buf, locktoken->ptr + 2, locktoken->used - 5); + + sqlite3_reset(stmt); + + sqlite3_bind_text(stmt, 1, + CONST_BUF_LEN(p->tmp_buf), + SQLITE_TRANSIENT); + + if (SQLITE_DONE != sqlite3_step(stmt)) { + log_error_write(srv, __FILE__, __LINE__, "ss", + "refresh lock:", sqlite3_errmsg(p->conf.sql)); + } + + webdav_lockdiscovery(srv, con, p->tmp_buf, "exclusive", "write", 0); + + con->http_status = 200; + con->file_finished = 1; + return HANDLER_FINISHED; + } else { + /* we need a lock-token to refresh */ + con->http_status = 400; + + return HANDLER_FINISHED; + } + } + break; +#else + con->http_status = 501; + return HANDLER_FINISHED; +#endif + case HTTP_METHOD_UNLOCK: +#ifdef USE_LOCKS + if (NULL != (ds = (data_string *)array_get_element(con->request.headers, "Lock-Token"))) { + buffer *locktoken = ds->value; + sqlite3_stmt *stmt = p->conf.stmt_remove_lock; + + /* remove the < > around the token */ + if (locktoken->used < 4) { + con->http_status = 400; + + return HANDLER_FINISHED; + } + + /** + * FIXME: + * + * if the resourse is locked: + * - by us: unlock + * - by someone else: 401 + * if the resource is not locked: + * - 412 + * */ + + buffer_copy_string_len(p->tmp_buf, locktoken->ptr + 1, locktoken->used - 3); + + sqlite3_reset(stmt); + + sqlite3_bind_text(stmt, 1, + CONST_BUF_LEN(p->tmp_buf), + SQLITE_TRANSIENT); + + sqlite3_bind_text(stmt, 2, + CONST_BUF_LEN(con->uri.path), + SQLITE_TRANSIENT); + + if (SQLITE_DONE != sqlite3_step(stmt)) { + log_error_write(srv, __FILE__, __LINE__, "ss", + "remove lock:", sqlite3_errmsg(p->conf.sql)); + } + + if (0 == sqlite3_changes(p->conf.sql)) { + con->http_status = 401; + } else { + con->http_status = 204; + } + return HANDLER_FINISHED; + } else { + /* we need a lock-token to unlock */ + con->http_status = 400; + + return HANDLER_FINISHED; + } + break; +#else + con->http_status = 501; + return HANDLER_FINISHED; +#endif default: break; } - + /* not found */ return HANDLER_GO_ON; } @@ -1838,17 +2494,18 @@ propmatch_cleanup: /* this function is called at dlopen() time and inits the callbacks */ +int mod_webdav_plugin_init(plugin *p); int mod_webdav_plugin_init(plugin *p) { p->version = LIGHTTPD_VERSION_ID; p->name = buffer_init_string("webdav"); - + p->init = mod_webdav_init; p->handle_uri_clean = mod_webdav_uri_handler; p->handle_physical = mod_webdav_subrequest_handler; p->set_defaults = mod_webdav_set_defaults; p->cleanup = mod_webdav_free; - + p->data = NULL; - + return 0; } diff --git a/src/network.c b/src/network.c index 922009f..f59f60d 100644 --- a/src/network.c +++ b/src/network.c @@ -1,3 +1,15 @@ +#include "network.h" +#include "fdevent.h" +#include "log.h" +#include "connections.h" +#include "plugin.h" +#include "joblist.h" +#include "configfile.h" + +#include "network_backends.h" +#include "sys-mmap.h" +#include "sys-socket.h" + #include <sys/types.h> #include <sys/stat.h> #include <sys/time.h> @@ -9,33 +21,42 @@ #include <stdlib.h> #include <assert.h> -#include "network.h" -#include "fdevent.h" -#include "log.h" -#include "connections.h" -#include "plugin.h" -#include "joblist.h" - -#include "network_backends.h" -#include "sys-mmap.h" -#include "sys-socket.h" +#ifdef USE_OPENSSL +# include <openssl/ssl.h> +# include <openssl/err.h> +# include <openssl/rand.h> +# include <openssl/dh.h> +# include <openssl/bn.h> + +# if OPENSSL_VERSION_NUMBER >= 0x0090800fL +# ifndef OPENSSL_NO_ECDH +# include <openssl/ecdh.h> +# endif +# endif +#endif #ifdef USE_OPENSSL -# include <openssl/ssl.h> -# include <openssl/err.h> -# include <openssl/rand.h> +static void ssl_info_callback(const SSL *ssl, int where, int ret) { + UNUSED(ret); + + if (0 != (where & SSL_CB_HANDSHAKE_START)) { + connection *con = SSL_get_app_data(ssl); + ++con->renegotiations; + } else if (0 != (where & SSL_CB_HANDSHAKE_DONE)) { + ssl->s3->flags |= SSL3_FLAGS_NO_RENEGOTIATE_CIPHERS; + } +} #endif -handler_t network_server_handle_fdevent(void *s, void *context, int revents) { - server *srv = (server *)s; +static handler_t network_server_handle_fdevent(server *srv, void *context, int revents) { server_socket *srv_socket = (server_socket *)context; connection *con; int loops = 0; - + UNUSED(context); - - if (revents != FDEVENT_IN) { - log_error_write(srv, __FILE__, __LINE__, "sdd", + + if (0 == (revents & FDEVENT_IN)) { + log_error_write(srv, __FILE__, __LINE__, "sdd", "strange event for server socket", srv_socket->fd, revents); @@ -44,12 +65,12 @@ handler_t network_server_handle_fdevent(void *s, void *context, int revents) { /* accept()s at most 100 connections directly * - * we jump out after 100 to give the waiting connections a chance */ + * we jump out after 100 to give the waiting connections a chance */ for (loops = 0; loops < 100 && NULL != (con = connection_accept(srv, srv_socket)); loops++) { handler_t r; - + connection_state_machine(srv, con); - + switch(r = plugins_call_handle_joblist(srv, con)) { case HANDLER_FINISHED: case HANDLER_GO_ON: @@ -62,7 +83,54 @@ handler_t network_server_handle_fdevent(void *s, void *context, int revents) { return HANDLER_GO_ON; } -int network_server_init(server *srv, buffer *host_token, specific_config *s) { +#if defined USE_OPENSSL && ! defined OPENSSL_NO_TLSEXT +static int network_ssl_servername_callback(SSL *ssl, int *al, server *srv) { + const char *servername; + connection *con = (connection *) SSL_get_app_data(ssl); + UNUSED(al); + + buffer_copy_string(con->uri.scheme, "https"); + + if (NULL == (servername = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name))) { +#if 0 + /* this "error" just means the client didn't support it */ + log_error_write(srv, __FILE__, __LINE__, "ss", "SSL:", + "failed to get TLS server name"); +#endif + return SSL_TLSEXT_ERR_NOACK; + } + buffer_copy_string(con->tlsext_server_name, servername); + buffer_to_lower(con->tlsext_server_name); + + /* Sometimes this is still set, confusing COMP_HTTP_HOST */ + buffer_reset(con->uri.authority); + + config_cond_cache_reset(srv, con); + config_setup_connection(srv, con); + + config_patch_connection(srv, con, COMP_SERVER_SOCKET); + config_patch_connection(srv, con, COMP_HTTP_SCHEME); + config_patch_connection(srv, con, COMP_HTTP_HOST); + + if (NULL == con->conf.ssl_ctx) { + /* ssl_ctx <=> pemfile was set <=> ssl_ctx got patched: so this should never happen */ + log_error_write(srv, __FILE__, __LINE__, "ssb", "SSL:", + "null SSL_CTX for TLS server name", con->tlsext_server_name); + return SSL_TLSEXT_ERR_ALERT_FATAL; + } + + /* switch to new SSL_CTX in reaction to a client's server_name extension */ + if (con->conf.ssl_ctx != SSL_set_SSL_CTX(ssl, con->conf.ssl_ctx)) { + log_error_write(srv, __FILE__, __LINE__, "ssb", "SSL:", + "failed to set SSL_CTX for TLS server name", con->tlsext_server_name); + return SSL_TLSEXT_ERR_ALERT_FATAL; + } + + return SSL_TLSEXT_ERR_OK; +} +#endif + +static int network_server_init(server *srv, buffer *host_token, specific_config *s) { int val; socklen_t addr_len; server_socket *srv_socket; @@ -72,18 +140,14 @@ int network_server_init(server *srv, buffer *host_token, specific_config *s) { buffer *b; int is_unix_domain_socket = 0; int fd; - -#ifdef SO_ACCEPTFILTER - struct accept_filter_arg afa; -#endif #ifdef __WIN32 WORD wVersionRequested; WSADATA wsaData; int err; - + wVersionRequested = MAKEWORD( 2, 2 ); - + err = WSAStartup( wVersionRequested, &wsaData ); if ( err != 0 ) { /* Tell the user that we could not find a usable */ @@ -91,37 +155,38 @@ int network_server_init(server *srv, buffer *host_token, specific_config *s) { return -1; } #endif - + srv_socket = calloc(1, sizeof(*srv_socket)); srv_socket->fd = -1; - + srv_socket->fde_ndx = -1; + srv_socket->srv_token = buffer_init(); buffer_copy_string_buffer(srv_socket->srv_token, host_token); - + b = buffer_init(); buffer_copy_string_buffer(b, host_token); - - /* ipv4:port + + /* ipv4:port * [ipv6]:port */ if (NULL == (sp = strrchr(b->ptr, ':'))) { log_error_write(srv, __FILE__, __LINE__, "sb", "value of $SERVER[\"socket\"] has to be \"ip:port\".", b); - - return -1; + + goto error_free_socket; } - + host = b->ptr; - + /* check for [ and ] */ if (b->ptr[0] == '[' && *(sp-1) == ']') { *(sp-1) = '\0'; host++; - + s->use_ipv6 = 1; } - + *(sp++) = '\0'; - + port = strtol(sp, NULL, 10); if (host[0] == '/') { @@ -129,57 +194,62 @@ int network_server_init(server *srv, buffer *host_token, specific_config *s) { is_unix_domain_socket = 1; } else if (port == 0 || port > 65535) { log_error_write(srv, __FILE__, __LINE__, "sd", "port out of range:", port); - - return -1; + + goto error_free_socket; } - + if (*host == '\0') host = NULL; if (is_unix_domain_socket) { #ifdef HAVE_SYS_UN_H srv_socket->addr.plain.sa_family = AF_UNIX; - + if (-1 == (srv_socket->fd = socket(srv_socket->addr.plain.sa_family, SOCK_STREAM, 0))) { log_error_write(srv, __FILE__, __LINE__, "ss", "socket failed:", strerror(errno)); - return -1; + goto error_free_socket; } #else log_error_write(srv, __FILE__, __LINE__, "s", "ERROR: Unix Domain sockets are not supported."); - return -1; + goto error_free_socket; #endif } #ifdef HAVE_IPV6 if (s->use_ipv6) { srv_socket->addr.plain.sa_family = AF_INET6; - + if (-1 == (srv_socket->fd = socket(srv_socket->addr.plain.sa_family, SOCK_STREAM, IPPROTO_TCP))) { log_error_write(srv, __FILE__, __LINE__, "ss", "socket failed:", strerror(errno)); - return -1; + goto error_free_socket; } srv_socket->use_ipv6 = 1; } #endif - + if (srv_socket->fd == -1) { srv_socket->addr.plain.sa_family = AF_INET; if (-1 == (srv_socket->fd = socket(srv_socket->addr.plain.sa_family, SOCK_STREAM, IPPROTO_TCP))) { log_error_write(srv, __FILE__, __LINE__, "ss", "socket failed:", strerror(errno)); - return -1; + goto error_free_socket; } } - + +#ifdef FD_CLOEXEC + /* set FD_CLOEXEC now, fdevent_fcntl_set is called later; needed for pipe-logger forks */ + fcntl(srv_socket->fd, F_SETFD, FD_CLOEXEC); +#endif + /* */ srv->cur_fds = srv_socket->fd; - + val = 1; if (setsockopt(srv_socket->fd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val)) < 0) { - log_error_write(srv, __FILE__, __LINE__, "ss", "socketsockopt failed:", strerror(errno)); - return -1; + log_error_write(srv, __FILE__, __LINE__, "ss", "socketsockopt(SO_REUSEADDR) failed:", strerror(errno)); + goto error_free_socket; } - + switch(srv_socket->addr.plain.sa_family) { #ifdef HAVE_IPV6 case AF_INET6: @@ -187,26 +257,37 @@ int network_server_init(server *srv, buffer *host_token, specific_config *s) { srv_socket->addr.ipv6.sin6_family = AF_INET6; if (host == NULL) { srv_socket->addr.ipv6.sin6_addr = in6addr_any; + log_error_write(srv, __FILE__, __LINE__, "s", "warning: please use server.use-ipv6 only for hostnames, not without server.bind / empty address; your config will break if the kernel default for IPV6_V6ONLY changes"); } else { struct addrinfo hints, *res; int r; - + + if (s->set_v6only) { + val = 1; + if (-1 == setsockopt(srv_socket->fd, IPPROTO_IPV6, IPV6_V6ONLY, &val, sizeof(val))) { + log_error_write(srv, __FILE__, __LINE__, "ss", "socketsockopt(IPV6_V6ONLY) failed:", strerror(errno)); + goto error_free_socket; + } + } else { + log_error_write(srv, __FILE__, __LINE__, "s", "warning: server.set-v6only will be removed soon, update your config to have different sockets for ipv4 and ipv6"); + } + memset(&hints, 0, sizeof(hints)); - + hints.ai_family = AF_INET6; hints.ai_socktype = SOCK_STREAM; hints.ai_protocol = IPPROTO_TCP; - + if (0 != (r = getaddrinfo(host, NULL, &hints, &res))) { - log_error_write(srv, __FILE__, __LINE__, - "sssss", "getaddrinfo failed: ", + log_error_write(srv, __FILE__, __LINE__, + "sssss", "getaddrinfo failed: ", gai_strerror(r), "'", host, "'"); - - return -1; + + goto error_free_socket; } - + memcpy(&(srv_socket->addr), res->ai_addr, res->ai_addrlen); - + freeaddrinfo(res); } srv_socket->addr.ipv6.sin6_port = htons(port); @@ -221,50 +302,50 @@ int network_server_init(server *srv, buffer *host_token, specific_config *s) { } else { struct hostent *he; if (NULL == (he = gethostbyname(host))) { - log_error_write(srv, __FILE__, __LINE__, - "sds", "gethostbyname failed: ", + log_error_write(srv, __FILE__, __LINE__, + "sds", "gethostbyname failed: ", h_errno, host); - return -1; + goto error_free_socket; } - + if (he->h_addrtype != AF_INET) { log_error_write(srv, __FILE__, __LINE__, "sd", "addr-type != AF_INET: ", he->h_addrtype); - return -1; + goto error_free_socket; } - + if (he->h_length != sizeof(struct in_addr)) { log_error_write(srv, __FILE__, __LINE__, "sd", "addr-length != sizeof(in_addr): ", he->h_length); - return -1; + goto error_free_socket; } - + memcpy(&(srv_socket->addr.ipv4.sin_addr.s_addr), he->h_addr_list[0], he->h_length); } srv_socket->addr.ipv4.sin_port = htons(port); - + addr_len = sizeof(struct sockaddr_in); - + break; case AF_UNIX: srv_socket->addr.un.sun_family = AF_UNIX; strcpy(srv_socket->addr.un.sun_path, host); - + #ifdef SUN_LEN addr_len = SUN_LEN(&srv_socket->addr.un); #else /* stevens says: */ - addr_len = strlen(host) + sizeof(srv_socket->addr.un.sun_family); + addr_len = strlen(host) + 1 + sizeof(srv_socket->addr.un.sun_family); #endif /* check if the socket exists and try to connect to it. */ if (-1 != (fd = connect(srv_socket->fd, (struct sockaddr *) &(srv_socket->addr), addr_len))) { close(fd); - log_error_write(srv, __FILE__, __LINE__, "ss", - "server socket is still in use:", + log_error_write(srv, __FILE__, __LINE__, "ss", + "server socket is still in use:", host); - return -1; + goto error_free_socket; } /* connect failed */ @@ -275,112 +356,63 @@ int network_server_init(server *srv, buffer *host_token, specific_config *s) { case ENOENT: break; default: - log_error_write(srv, __FILE__, __LINE__, "sds", - "testing socket failed:", + log_error_write(srv, __FILE__, __LINE__, "sds", + "testing socket failed:", host, strerror(errno)); - return -1; + goto error_free_socket; } break; default: - addr_len = 0; - - return -1; + goto error_free_socket; } - + if (0 != bind(srv_socket->fd, (struct sockaddr *) &(srv_socket->addr), addr_len)) { switch(srv_socket->addr.plain.sa_family) { case AF_UNIX: - log_error_write(srv, __FILE__, __LINE__, "sds", - "can't bind to socket:", + log_error_write(srv, __FILE__, __LINE__, "sds", + "can't bind to socket:", host, strerror(errno)); break; default: - log_error_write(srv, __FILE__, __LINE__, "ssds", - "can't bind to port:", + log_error_write(srv, __FILE__, __LINE__, "ssds", + "can't bind to port:", host, port, strerror(errno)); break; } - return -1; + goto error_free_socket; } - + if (-1 == listen(srv_socket->fd, 128 * 8)) { log_error_write(srv, __FILE__, __LINE__, "ss", "listen failed: ", strerror(errno)); - return -1; + goto error_free_socket; } - + if (s->is_ssl) { #ifdef USE_OPENSSL - if (srv->ssl_is_init == 0) { - SSL_load_error_strings(); - SSL_library_init(); - srv->ssl_is_init = 1; - - if (0 == RAND_status()) { - log_error_write(srv, __FILE__, __LINE__, "ss", "SSL:", - "not enough entropy in the pool"); - return -1; - } - } - - if (NULL == (s->ssl_ctx = SSL_CTX_new(SSLv23_server_method()))) { - log_error_write(srv, __FILE__, __LINE__, "ss", "SSL:", - ERR_error_string(ERR_get_error(), NULL)); - return -1; - } - - if (buffer_is_empty(s->ssl_pemfile)) { + if (NULL == (srv_socket->ssl_ctx = s->ssl_ctx)) { log_error_write(srv, __FILE__, __LINE__, "s", "ssl.pemfile has to be set"); - return -1; - } - - if (!buffer_is_empty(s->ssl_ca_file)) { - if (1 != SSL_CTX_load_verify_locations(s->ssl_ctx, s->ssl_ca_file->ptr, NULL)) { - log_error_write(srv, __FILE__, __LINE__, "ssb", "SSL:", - ERR_error_string(ERR_get_error(), NULL), s->ssl_ca_file); - return -1; - } - } - - if (SSL_CTX_use_certificate_file(s->ssl_ctx, s->ssl_pemfile->ptr, SSL_FILETYPE_PEM) < 0) { - log_error_write(srv, __FILE__, __LINE__, "ssb", "SSL:", - ERR_error_string(ERR_get_error(), NULL), s->ssl_pemfile); - return -1; - } - - if (SSL_CTX_use_PrivateKey_file (s->ssl_ctx, s->ssl_pemfile->ptr, SSL_FILETYPE_PEM) < 0) { - log_error_write(srv, __FILE__, __LINE__, "ssb", "SSL:", - ERR_error_string(ERR_get_error(), NULL), s->ssl_pemfile); - return -1; - } - - if (SSL_CTX_check_private_key(s->ssl_ctx) != 1) { - log_error_write(srv, __FILE__, __LINE__, "sssb", "SSL:", - "Private key does not match the certificate public key, reason:", - ERR_error_string(ERR_get_error(), NULL), - s->ssl_pemfile); - return -1; + goto error_free_socket; } - srv_socket->ssl_ctx = s->ssl_ctx; #else - - buffer_free(srv_socket->srv_token); - free(srv_socket); - - buffer_free(b); - - log_error_write(srv, __FILE__, __LINE__, "ss", "SSL:", + + log_error_write(srv, __FILE__, __LINE__, "ss", "SSL:", "ssl requested but openssl support is not compiled in"); - - return -1; + + goto error_free_socket; +#endif +#ifdef TCP_DEFER_ACCEPT + } else if (s->defer_accept) { + int v = s->defer_accept; + if (-1 == setsockopt(srv_socket->fd, IPPROTO_TCP, TCP_DEFER_ACCEPT, &v, sizeof(v))) { + log_error_write(srv, __FILE__, __LINE__, "ss", "can't set TCP_DEFER_ACCEPT: ", strerror(errno)); + } #endif } else { #ifdef SO_ACCEPTFILTER - /* - * FreeBSD accf_http filter - * - */ + /* FreeBSD accf_http filter */ + struct accept_filter_arg afa; memset(&afa, 0, sizeof(afa)); strcpy(afa.af_name, "httpready"); if (setsockopt(srv_socket->fd, SOL_SOCKET, SO_ACCEPTFILTER, &afa, sizeof(afa)) < 0) { @@ -390,10 +422,9 @@ int network_server_init(server *srv, buffer *host_token, specific_config *s) { } #endif } - + srv_socket->is_ssl = s->is_ssl; - srv_socket->fde_ndx = -1; - + if (srv->srv_sockets.size == 0) { srv->srv_sockets.size = 4; srv->srv_sockets.used = 0; @@ -402,36 +433,53 @@ int network_server_init(server *srv, buffer *host_token, specific_config *s) { srv->srv_sockets.size += 4; srv->srv_sockets.ptr = realloc(srv->srv_sockets.ptr, srv->srv_sockets.size * sizeof(server_socket)); } - + srv->srv_sockets.ptr[srv->srv_sockets.used++] = srv_socket; - + buffer_free(b); - + return 0; + +error_free_socket: + if (srv_socket->fd != -1) { + /* check if server fd are already registered */ + if (srv_socket->fde_ndx != -1) { + fdevent_event_del(srv->ev, &(srv_socket->fde_ndx), srv_socket->fd); + fdevent_unregister(srv->ev, srv_socket->fd); + } + + close(srv_socket->fd); + } + buffer_free(srv_socket->srv_token); + free(srv_socket); + + buffer_free(b); + + return -1; } int network_close(server *srv) { size_t i; for (i = 0; i < srv->srv_sockets.used; i++) { server_socket *srv_socket = srv->srv_sockets.ptr[i]; - + if (srv_socket->fd != -1) { /* check if server fd are already registered */ if (srv_socket->fde_ndx != -1) { fdevent_event_del(srv->ev, &(srv_socket->fde_ndx), srv_socket->fd); fdevent_unregister(srv->ev, srv_socket->fd); } - + close(srv_socket->fd); } - + buffer_free(srv_socket->srv_token); - + free(srv_socket); } - + free(srv->srv_sockets.ptr); - + return 0; } @@ -448,11 +496,62 @@ int network_init(server *srv) { buffer *b; size_t i; network_backend_t backend; - - struct nb_map { - network_backend_t nb; - const char *name; - } network_backends[] = { + +#if OPENSSL_VERSION_NUMBER >= 0x0090800fL +#ifndef OPENSSL_NO_ECDH + EC_KEY *ecdh; + int nid; +#endif +#endif + +#ifdef USE_OPENSSL + DH *dh; + BIO *bio; + + /* 1024-bit MODP Group with 160-bit prime order subgroup (RFC5114) + * -----BEGIN DH PARAMETERS----- + * MIIBDAKBgQCxC4+WoIDgHd6S3l6uXVTsUsmfvPsGo8aaap3KUtI7YWBz4oZ1oj0Y + * mDjvHi7mUsAT7LSuqQYRIySXXDzUm4O/rMvdfZDEvXCYSI6cIZpzck7/1vrlZEc4 + * +qMaT/VbzMChUa9fDci0vUW/N982XBpl5oz9p21NpwjfH7K8LkpDcQKBgQCk0cvV + * w/00EmdlpELvuZkF+BBN0lisUH/WQGz/FCZtMSZv6h5cQVZLd35pD1UE8hMWAhe0 + * sBuIal6RVH+eJ0n01/vX07mpLuGQnQ0iY/gKdqaiTAh6CR9THb8KAWm2oorWYqTR + * jnOvoy13nVkY0IvIhY9Nzvl8KiSFXm7rIrOy5QICAKA= + * -----END DH PARAMETERS----- + */ + + static const unsigned char dh1024_p[]={ + 0xB1,0x0B,0x8F,0x96,0xA0,0x80,0xE0,0x1D,0xDE,0x92,0xDE,0x5E, + 0xAE,0x5D,0x54,0xEC,0x52,0xC9,0x9F,0xBC,0xFB,0x06,0xA3,0xC6, + 0x9A,0x6A,0x9D,0xCA,0x52,0xD2,0x3B,0x61,0x60,0x73,0xE2,0x86, + 0x75,0xA2,0x3D,0x18,0x98,0x38,0xEF,0x1E,0x2E,0xE6,0x52,0xC0, + 0x13,0xEC,0xB4,0xAE,0xA9,0x06,0x11,0x23,0x24,0x97,0x5C,0x3C, + 0xD4,0x9B,0x83,0xBF,0xAC,0xCB,0xDD,0x7D,0x90,0xC4,0xBD,0x70, + 0x98,0x48,0x8E,0x9C,0x21,0x9A,0x73,0x72,0x4E,0xFF,0xD6,0xFA, + 0xE5,0x64,0x47,0x38,0xFA,0xA3,0x1A,0x4F,0xF5,0x5B,0xCC,0xC0, + 0xA1,0x51,0xAF,0x5F,0x0D,0xC8,0xB4,0xBD,0x45,0xBF,0x37,0xDF, + 0x36,0x5C,0x1A,0x65,0xE6,0x8C,0xFD,0xA7,0x6D,0x4D,0xA7,0x08, + 0xDF,0x1F,0xB2,0xBC,0x2E,0x4A,0x43,0x71, + }; + + static const unsigned char dh1024_g[]={ + 0xA4,0xD1,0xCB,0xD5,0xC3,0xFD,0x34,0x12,0x67,0x65,0xA4,0x42, + 0xEF,0xB9,0x99,0x05,0xF8,0x10,0x4D,0xD2,0x58,0xAC,0x50,0x7F, + 0xD6,0x40,0x6C,0xFF,0x14,0x26,0x6D,0x31,0x26,0x6F,0xEA,0x1E, + 0x5C,0x41,0x56,0x4B,0x77,0x7E,0x69,0x0F,0x55,0x04,0xF2,0x13, + 0x16,0x02,0x17,0xB4,0xB0,0x1B,0x88,0x6A,0x5E,0x91,0x54,0x7F, + 0x9E,0x27,0x49,0xF4,0xD7,0xFB,0xD7,0xD3,0xB9,0xA9,0x2E,0xE1, + 0x90,0x9D,0x0D,0x22,0x63,0xF8,0x0A,0x76,0xA6,0xA2,0x4C,0x08, + 0x7A,0x09,0x1F,0x53,0x1D,0xBF,0x0A,0x01,0x69,0xB6,0xA2,0x8A, + 0xD6,0x62,0xA4,0xD1,0x8E,0x73,0xAF,0xA3,0x2D,0x77,0x9D,0x59, + 0x18,0xD0,0x8B,0xC8,0x85,0x8F,0x4D,0xCE,0xF9,0x7C,0x2A,0x24, + 0x85,0x5E,0x6E,0xEB,0x22,0xB3,0xB2,0xE5, + }; +#endif + + struct nb_map { + network_backend_t nb; + const char *name; + } network_backends[] = { /* lowest id wins */ #if defined USE_LINUX_SENDFILE { NETWORK_BACKEND_LINUX_SENDFILE, "linux-sendfile" }, @@ -469,18 +568,218 @@ int network_init(server *srv) { { NETWORK_BACKEND_WRITE, "write" }, { NETWORK_BACKEND_UNSET, NULL } }; - + +#ifdef USE_OPENSSL + /* load SSL certificates */ + for (i = 0; i < srv->config_context->used; i++) { + specific_config *s = srv->config_storage[i]; +#ifndef SSL_OP_NO_COMPRESSION +# define SSL_OP_NO_COMPRESSION 0 +#endif + long ssloptions = + SSL_OP_ALL | SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION | SSL_OP_NO_COMPRESSION; + + if (buffer_is_empty(s->ssl_pemfile)) continue; + +#ifdef OPENSSL_NO_TLSEXT + { + data_config *dc = (data_config *)srv->config_context->data[i]; + if (COMP_HTTP_HOST == dc->comp) { + log_error_write(srv, __FILE__, __LINE__, "ss", "SSL:", + "can't use ssl.pemfile with $HTTP[\"host\"], openssl version does not support TLS extensions"); + return -1; + } + } +#endif + + if (srv->ssl_is_init == 0) { + SSL_load_error_strings(); + SSL_library_init(); + OpenSSL_add_all_algorithms(); + srv->ssl_is_init = 1; + + if (0 == RAND_status()) { + log_error_write(srv, __FILE__, __LINE__, "ss", "SSL:", + "not enough entropy in the pool"); + return -1; + } + } + + if (NULL == (s->ssl_ctx = SSL_CTX_new(SSLv23_server_method()))) { + log_error_write(srv, __FILE__, __LINE__, "ss", "SSL:", + ERR_error_string(ERR_get_error(), NULL)); + return -1; + } + + SSL_CTX_set_options(s->ssl_ctx, ssloptions); + SSL_CTX_set_info_callback(s->ssl_ctx, ssl_info_callback); + + if (!s->ssl_use_sslv2) { + /* disable SSLv2 */ + if (!(SSL_OP_NO_SSLv2 & SSL_CTX_set_options(s->ssl_ctx, SSL_OP_NO_SSLv2))) { + log_error_write(srv, __FILE__, __LINE__, "ss", "SSL:", + ERR_error_string(ERR_get_error(), NULL)); + return -1; + } + } + + if (!s->ssl_use_sslv3) { + /* disable SSLv3 */ + if (!(SSL_OP_NO_SSLv3 & SSL_CTX_set_options(s->ssl_ctx, SSL_OP_NO_SSLv3))) { + log_error_write(srv, __FILE__, __LINE__, "ss", "SSL:", + ERR_error_string(ERR_get_error(), NULL)); + return -1; + } + } + + if (!buffer_is_empty(s->ssl_cipher_list)) { + /* Disable support for low encryption ciphers */ + if (SSL_CTX_set_cipher_list(s->ssl_ctx, s->ssl_cipher_list->ptr) != 1) { + log_error_write(srv, __FILE__, __LINE__, "ss", "SSL:", + ERR_error_string(ERR_get_error(), NULL)); + return -1; + } + + if (s->ssl_honor_cipher_order) { + SSL_CTX_set_options(s->ssl_ctx, SSL_OP_CIPHER_SERVER_PREFERENCE); + } + } + + /* Support for Diffie-Hellman key exchange */ + if (!buffer_is_empty(s->ssl_dh_file)) { + /* DH parameters from file */ + bio = BIO_new_file((char *) s->ssl_dh_file->ptr, "r"); + if (bio == NULL) { + log_error_write(srv, __FILE__, __LINE__, "ss", "SSL: Unable to open file", s->ssl_dh_file->ptr); + return -1; + } + dh = PEM_read_bio_DHparams(bio, NULL, NULL, NULL); + BIO_free(bio); + if (dh == NULL) { + log_error_write(srv, __FILE__, __LINE__, "ss", "SSL: PEM_read_bio_DHparams failed", s->ssl_dh_file->ptr); + return -1; + } + } else { + /* Default DH parameters from RFC5114 */ + dh = DH_new(); + if (dh == NULL) { + log_error_write(srv, __FILE__, __LINE__, "s", "SSL: DH_new () failed"); + return -1; + } + dh->p = BN_bin2bn(dh1024_p,sizeof(dh1024_p), NULL); + dh->g = BN_bin2bn(dh1024_g,sizeof(dh1024_g), NULL); + dh->length = 160; + if ((dh->p == NULL) || (dh->g == NULL)) { + DH_free(dh); + log_error_write(srv, __FILE__, __LINE__, "s", "SSL: BN_bin2bn () failed"); + return -1; + } + } + SSL_CTX_set_tmp_dh(s->ssl_ctx,dh); + SSL_CTX_set_options(s->ssl_ctx,SSL_OP_SINGLE_DH_USE); + DH_free(dh); + +#if OPENSSL_VERSION_NUMBER >= 0x0090800fL +#ifndef OPENSSL_NO_ECDH + /* Support for Elliptic-Curve Diffie-Hellman key exchange */ + if (!buffer_is_empty(s->ssl_ec_curve)) { + /* OpenSSL only supports the "named curves" from RFC 4492, section 5.1.1. */ + nid = OBJ_sn2nid((char *) s->ssl_ec_curve->ptr); + if (nid == 0) { + log_error_write(srv, __FILE__, __LINE__, "ss", "SSL: Unknown curve name", s->ssl_ec_curve->ptr); + return -1; + } + } else { + /* Default curve */ + nid = OBJ_sn2nid("prime256v1"); + } + ecdh = EC_KEY_new_by_curve_name(nid); + if (ecdh == NULL) { + log_error_write(srv, __FILE__, __LINE__, "ss", "SSL: Unable to create curve", s->ssl_ec_curve->ptr); + return -1; + } + SSL_CTX_set_tmp_ecdh(s->ssl_ctx,ecdh); + SSL_CTX_set_options(s->ssl_ctx,SSL_OP_SINGLE_ECDH_USE); + EC_KEY_free(ecdh); +#endif +#endif + + if (!buffer_is_empty(s->ssl_ca_file)) { + if (1 != SSL_CTX_load_verify_locations(s->ssl_ctx, s->ssl_ca_file->ptr, NULL)) { + log_error_write(srv, __FILE__, __LINE__, "ssb", "SSL:", + ERR_error_string(ERR_get_error(), NULL), s->ssl_ca_file); + return -1; + } + if (s->ssl_verifyclient) { + STACK_OF(X509_NAME) *certs = SSL_load_client_CA_file(s->ssl_ca_file->ptr); + if (!certs) { + log_error_write(srv, __FILE__, __LINE__, "ssb", "SSL:", + ERR_error_string(ERR_get_error(), NULL), s->ssl_ca_file); + } + if (SSL_CTX_set_session_id_context(s->ssl_ctx, (void*) &srv, sizeof(srv)) != 1) { + log_error_write(srv, __FILE__, __LINE__, "ss", "SSL:", + ERR_error_string(ERR_get_error(), NULL)); + return -1; + } + SSL_CTX_set_client_CA_list(s->ssl_ctx, certs); + SSL_CTX_set_verify( + s->ssl_ctx, + SSL_VERIFY_PEER | (s->ssl_verifyclient_enforce ? SSL_VERIFY_FAIL_IF_NO_PEER_CERT : 0), + NULL + ); + SSL_CTX_set_verify_depth(s->ssl_ctx, s->ssl_verifyclient_depth); + } + } else if (s->ssl_verifyclient) { + log_error_write( + srv, __FILE__, __LINE__, "s", + "SSL: You specified ssl.verifyclient.activate but no ca_file" + ); + } + + if (SSL_CTX_use_certificate_file(s->ssl_ctx, s->ssl_pemfile->ptr, SSL_FILETYPE_PEM) < 0) { + log_error_write(srv, __FILE__, __LINE__, "ssb", "SSL:", + ERR_error_string(ERR_get_error(), NULL), s->ssl_pemfile); + return -1; + } + + if (SSL_CTX_use_PrivateKey_file (s->ssl_ctx, s->ssl_pemfile->ptr, SSL_FILETYPE_PEM) < 0) { + log_error_write(srv, __FILE__, __LINE__, "ssb", "SSL:", + ERR_error_string(ERR_get_error(), NULL), s->ssl_pemfile); + return -1; + } + + if (SSL_CTX_check_private_key(s->ssl_ctx) != 1) { + log_error_write(srv, __FILE__, __LINE__, "sssb", "SSL:", + "Private key does not match the certificate public key, reason:", + ERR_error_string(ERR_get_error(), NULL), + s->ssl_pemfile); + return -1; + } + SSL_CTX_set_default_read_ahead(s->ssl_ctx, 1); + SSL_CTX_set_mode(s->ssl_ctx, SSL_CTX_get_mode(s->ssl_ctx) | SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER); + +# ifndef OPENSSL_NO_TLSEXT + if (!SSL_CTX_set_tlsext_servername_callback(s->ssl_ctx, network_ssl_servername_callback) || + !SSL_CTX_set_tlsext_servername_arg(s->ssl_ctx, srv)) { + log_error_write(srv, __FILE__, __LINE__, "ss", "SSL:", + "failed to initialize TLS servername callback, openssl library does not support TLS servername extension"); + return -1; + } +# endif + } +#endif + b = buffer_init(); - + buffer_copy_string_buffer(b, srv->srvconf.bindhost); - buffer_append_string(b, ":"); + buffer_append_string_len(b, CONST_STR_LEN(":")); buffer_append_long(b, srv->srvconf.port); - + if (0 != network_server_init(srv, b, srv->config_storage[0])) { return -1; } buffer_free(b); - + #ifdef USE_OPENSSL srv->network_ssl_backend_write = network_write_chunkqueue_openssl; #endif @@ -500,8 +799,8 @@ int network_init(server *srv) { if (NULL == network_backends[i].name) { /* we don't know it */ - log_error_write(srv, __FILE__, __LINE__, "sb", - "server.network-backend has a unknown value:", + log_error_write(srv, __FILE__, __LINE__, "sb", + "server.network-backend has a unknown value:", srv->srvconf.network_backend); return -1; @@ -519,17 +818,17 @@ int network_init(server *srv) { #endif #ifdef USE_LINUX_SENDFILE case NETWORK_BACKEND_LINUX_SENDFILE: - srv->network_backend_write = network_write_chunkqueue_linuxsendfile; + srv->network_backend_write = network_write_chunkqueue_linuxsendfile; break; #endif #ifdef USE_FREEBSD_SENDFILE case NETWORK_BACKEND_FREEBSD_SENDFILE: - srv->network_backend_write = network_write_chunkqueue_freebsdsendfile; + srv->network_backend_write = network_write_chunkqueue_freebsdsendfile; break; #endif #ifdef USE_SOLARIS_SENDFILEV case NETWORK_BACKEND_SOLARIS_SENDFILEV: - srv->network_backend_write = network_write_chunkqueue_solarissendfilev; + srv->network_backend_write = network_write_chunkqueue_solarissendfilev; break; #endif default: @@ -540,63 +839,85 @@ int network_init(server *srv) { for (i = 1; i < srv->config_context->used; i++) { data_config *dc = (data_config *)srv->config_context->data[i]; specific_config *s = srv->config_storage[i]; - + size_t j; + /* not our stage */ if (COMP_SERVER_SOCKET != dc->comp) continue; - - if (dc->cond != CONFIG_COND_EQ) { - log_error_write(srv, __FILE__, __LINE__, "s", "only == is allowed for $SERVER[\"socket\"]."); - - return -1; + + if (dc->cond != CONFIG_COND_EQ) continue; + + /* check if we already know this socket, + * if yes, don't init it */ + for (j = 0; j < srv->srv_sockets.used; j++) { + if (buffer_is_equal(srv->srv_sockets.ptr[j]->srv_token, dc->string)) { + break; + } } - - if (0 != network_server_init(srv, dc->string, s)) { - return -1; + + if (j == srv->srv_sockets.used) { + if (0 != network_server_init(srv, dc->string, s)) return -1; } } - - + return 0; } int network_register_fdevents(server *srv) { size_t i; - + if (-1 == fdevent_reset(srv->ev)) { return -1; } - + /* register fdevents after reset */ for (i = 0; i < srv->srv_sockets.used; i++) { server_socket *srv_socket = srv->srv_sockets.ptr[i]; - + fdevent_register(srv->ev, srv_socket->fd, network_server_handle_fdevent, srv_socket); - fdevent_event_add(srv->ev, &(srv_socket->fde_ndx), srv_socket->fd, FDEVENT_IN); + fdevent_event_set(srv->ev, &(srv_socket->fde_ndx), srv_socket->fd, FDEVENT_IN); } return 0; } -int network_write_chunkqueue(server *srv, connection *con, chunkqueue *cq) { +int network_write_chunkqueue(server *srv, connection *con, chunkqueue *cq, off_t max_bytes) { int ret = -1; off_t written = 0; -#ifdef TCP_CORK +#ifdef TCP_CORK int corked = 0; #endif server_socket *srv_socket = con->srv_socket; - if (con->conf.global_kbytes_per_second && - *(con->conf.global_bytes_per_second_cnt_ptr) > con->conf.global_kbytes_per_second * 1024) { - /* we reached the global traffic limit */ + if (con->conf.global_kbytes_per_second) { + off_t limit = con->conf.global_kbytes_per_second * 1024 - *(con->conf.global_bytes_per_second_cnt_ptr); + if (limit <= 0) { + /* we reached the global traffic limit */ + + con->traffic_limit_reached = 1; + joblist_append(srv, con); + + return 1; + } else { + if (max_bytes > limit) max_bytes = limit; + } + } + + if (con->conf.kbytes_per_second) { + off_t limit = con->conf.kbytes_per_second * 1024 - con->bytes_written_cur_second; + if (limit <= 0) { + /* we reached the traffic limit */ - con->traffic_limit_reached = 1; - joblist_append(srv, con); + con->traffic_limit_reached = 1; + joblist_append(srv, con); - return 1; - } + return 1; + } else { + if (max_bytes > limit) max_bytes = limit; + } + } written = cq->bytes_out; -#ifdef TCP_CORK +#ifdef TCP_CORK /* Linux: put a cork into the socket as we want to combine the write() calls * but only if we really have multiple chunks */ @@ -605,20 +926,20 @@ int network_write_chunkqueue(server *srv, connection *con, chunkqueue *cq) { setsockopt(con->fd, IPPROTO_TCP, TCP_CORK, &corked, sizeof(corked)); } #endif - + if (srv_socket->is_ssl) { #ifdef USE_OPENSSL - ret = srv->network_ssl_backend_write(srv, con, con->ssl, cq); + ret = srv->network_ssl_backend_write(srv, con, con->ssl, cq, max_bytes); #endif } else { - ret = srv->network_backend_write(srv, con, con->fd, cq); + ret = srv->network_backend_write(srv, con, con->fd, cq, max_bytes); } - + if (ret >= 0) { chunkqueue_remove_finished_chunks(cq); ret = chunkqueue_is_empty(cq) ? 0 : 1; } - + #ifdef TCP_CORK if (corked) { corked = 0; @@ -631,13 +952,6 @@ int network_write_chunkqueue(server *srv, connection *con, chunkqueue *cq) { con->bytes_written_cur_second += written; *(con->conf.global_bytes_per_second_cnt_ptr) += written; - - if (con->conf.kbytes_per_second && - (con->bytes_written_cur_second > con->conf.kbytes_per_second * 1024)) { - /* we reached the traffic limit */ - - con->traffic_limit_reached = 1; - joblist_append(srv, con); - } + return ret; } diff --git a/src/network.h b/src/network.h index 99c7596..d9d4e7a 100644 --- a/src/network.h +++ b/src/network.h @@ -3,7 +3,7 @@ #include "server.h" -int network_write_chunkqueue(server *srv, connection *con, chunkqueue *c); +int network_write_chunkqueue(server *srv, connection *con, chunkqueue *c, off_t max_bytes); int network_init(server *srv); int network_close(server *srv); diff --git a/src/network_backends.h b/src/network_backends.h index 94650a7..5813253 100644 --- a/src/network_backends.h +++ b/src/network_backends.h @@ -2,8 +2,9 @@ #define _NETWORK_BACKENDS_H_ #ifdef HAVE_CONFIG_H -#include "config.h" +# include "config.h" #endif +#include "settings.h" #include <sys/types.h> @@ -14,7 +15,7 @@ # include <sys/uio.h> #endif -#if defined HAVE_SYS_UIO_H && defined HAVE_SENDFILE && defined HAVE_WRITEV && defined(__FreeBSD__) +#if defined HAVE_SYS_UIO_H && defined HAVE_SENDFILE && defined HAVE_WRITEV && (defined(__FreeBSD__) || defined(__DragonFly__)) # define USE_FREEBSD_SENDFILE # include <sys/uio.h> #endif @@ -30,7 +31,7 @@ # include <sys/uio.h> #endif -#if defined HAVE_SYS_MMAN_H && defined HAVE_MMAP +#if defined HAVE_SYS_MMAN_H && defined HAVE_MMAP && defined ENABLE_MMAP # define USE_MMAP # include <sys/mman.h> /* NetBSD 1.3.x needs it */ @@ -45,14 +46,19 @@ #include "base.h" +/* return values: + * >= 0 : no error + * -1 : error (on our side) + * -2 : remote close + */ -int network_write_chunkqueue_write(server *srv, connection *con, int fd, chunkqueue *cq); -int network_write_chunkqueue_writev(server *srv, connection *con, int fd, chunkqueue *cq); -int network_write_chunkqueue_linuxsendfile(server *srv, connection *con, int fd, chunkqueue *cq); -int network_write_chunkqueue_freebsdsendfile(server *srv, connection *con, int fd, chunkqueue *cq); -int network_write_chunkqueue_solarissendfilev(server *srv, connection *con, int fd, chunkqueue *cq); +int network_write_chunkqueue_write(server *srv, connection *con, int fd, chunkqueue *cq, off_t max_bytes); +int network_write_chunkqueue_writev(server *srv, connection *con, int fd, chunkqueue *cq, off_t max_bytes); +int network_write_chunkqueue_linuxsendfile(server *srv, connection *con, int fd, chunkqueue *cq, off_t max_bytes); +int network_write_chunkqueue_freebsdsendfile(server *srv, connection *con, int fd, chunkqueue *cq, off_t max_bytes); +int network_write_chunkqueue_solarissendfilev(server *srv, connection *con, int fd, chunkqueue *cq, off_t max_bytes); #ifdef USE_OPENSSL -int network_write_chunkqueue_openssl(server *srv, connection *con, SSL *ssl, chunkqueue *cq); +int network_write_chunkqueue_openssl(server *srv, connection *con, SSL *ssl, chunkqueue *cq, off_t max_bytes); #endif #endif diff --git a/src/network_freebsd_sendfile.c b/src/network_freebsd_sendfile.c index a6994c0..7b165fc 100644 --- a/src/network_freebsd_sendfile.c +++ b/src/network_freebsd_sendfile.c @@ -2,6 +2,11 @@ #ifdef USE_FREEBSD_SENDFILE +#include "network.h" +#include "fdevent.h" +#include "log.h" +#include "stat_cache.h" + #include <sys/types.h> #include <sys/socket.h> #include <sys/stat.h> @@ -18,46 +23,38 @@ #include <string.h> #include <stdlib.h> -#include "network.h" -#include "fdevent.h" -#include "log.h" -#include "stat_cache.h" - #ifndef UIO_MAXIOV -# ifdef __FreeBSD__ -/* FreeBSD 4.7, 4.9 defined it in sys/uio.h only if _KERNEL is specified */ +# if defined(__FreeBSD__) || defined(__DragonFly__) +/* FreeBSD 4.7, 4.9 defined it in sys/uio.h only if _KERNEL is specified */ # define UIO_MAXIOV 1024 # endif #endif -int network_write_chunkqueue_freebsdsendfile(server *srv, connection *con, int fd, chunkqueue *cq) { +int network_write_chunkqueue_freebsdsendfile(server *srv, connection *con, int fd, chunkqueue *cq, off_t max_bytes) { chunk *c; - size_t chunks_written = 0; - - for(c = cq->first; c; c = c->next, chunks_written++) { + + for(c = cq->first; (max_bytes > 0) && (NULL != c); c = c->next) { int chunk_finished = 0; - + switch(c->type) { case MEM_CHUNK: { char * offset; - size_t toSend; + off_t toSend; ssize_t r; - + size_t num_chunks, i; struct iovec chunks[UIO_MAXIOV]; chunk *tc; size_t num_bytes = 0; - - /* we can't send more then SSIZE_MAX bytes in one chunk */ - - /* build writev list - * + + /* build writev list + * * 1. limit: num_chunks < UIO_MAXIOV - * 2. limit: num_bytes < SSIZE_MAX + * 2. limit: num_bytes < max_bytes */ for(num_chunks = 0, tc = c; tc && tc->type == MEM_CHUNK && num_chunks < UIO_MAXIOV; num_chunks++, tc = tc->next); - + for(tc = c, i = 0; i < num_chunks; tc = tc->next, i++) { if (tc->mem->used == 0) { chunks[i].iov_base = tc->mem->ptr; @@ -65,143 +62,160 @@ int network_write_chunkqueue_freebsdsendfile(server *srv, connection *con, int f } else { offset = tc->mem->ptr + tc->offset; toSend = tc->mem->used - 1 - tc->offset; - + chunks[i].iov_base = offset; - + /* protect the return value of writev() */ - if (toSend > SSIZE_MAX || - num_bytes + toSend > SSIZE_MAX) { - chunks[i].iov_len = SSIZE_MAX - num_bytes; - + if (toSend > max_bytes || + (off_t) num_bytes + toSend > max_bytes) { + chunks[i].iov_len = max_bytes - num_bytes; + num_chunks = i + 1; break; } else { chunks[i].iov_len = toSend; } - + num_bytes += toSend; } } - + if ((r = writev(fd, chunks, num_chunks)) < 0) { switch (errno) { case EAGAIN: case EINTR: r = 0; break; + case ENOTCONN: case EPIPE: case ECONNRESET: return -2; default: - log_error_write(srv, __FILE__, __LINE__, "ssd", + log_error_write(srv, __FILE__, __LINE__, "ssd", "writev failed:", strerror(errno), fd); - + return -1; } r = 0; } - + /* check which chunks have been written */ cq->bytes_out += r; - + max_bytes -= r; + for(i = 0, tc = c; i < num_chunks; i++, tc = tc->next) { if (r >= (ssize_t)chunks[i].iov_len) { /* written */ r -= chunks[i].iov_len; tc->offset += chunks[i].iov_len; - + if (chunk_finished) { /* skip the chunks from further touches */ - chunks_written++; c = c->next; } else { /* chunks_written + c = c->next is done in the for()*/ - chunk_finished++; + chunk_finished = 1; } } else { /* partially written */ - + tc->offset += r; chunk_finished = 0; - + break; } } - + break; } case FILE_CHUNK: { off_t offset, r; - size_t toSend; + off_t toSend; stat_cache_entry *sce = NULL; - int ifd; - + if (HANDLER_ERROR == stat_cache_get_entry(srv, con, c->file.name, &sce)) { log_error_write(srv, __FILE__, __LINE__, "sb", strerror(errno), c->file.name); return -1; } - + offset = c->file.start + c->offset; - /* limit the toSend to 2^31-1 bytes in a chunk */ - toSend = c->file.length - c->offset > ((1 << 30) - 1) ? - ((1 << 30) - 1) : c->file.length - c->offset; - - if (offset > sce->st.st_size) { - log_error_write(srv, __FILE__, __LINE__, "sb", "file was shrinked:", c->file.name); - - return -1; - } - - if (-1 == (ifd = open(c->file.name->ptr, O_RDONLY))) { - log_error_write(srv, __FILE__, __LINE__, "ss", "open failed: ", strerror(errno)); - - return -1; + toSend = c->file.length - c->offset; + if (toSend > max_bytes) toSend = max_bytes; + + if (-1 == c->file.fd) { + if (-1 == (c->file.fd = open(c->file.name->ptr, O_RDONLY))) { + log_error_write(srv, __FILE__, __LINE__, "ss", "open failed: ", strerror(errno)); + + return -1; + } + +#ifdef FD_CLOEXEC + fcntl(c->file.fd, F_SETFD, FD_CLOEXEC); +#endif } - + r = 0; - + /* FreeBSD sendfile() */ - if (-1 == sendfile(ifd, fd, offset, toSend, NULL, &r, 0)) { + if (-1 == sendfile(c->file.fd, fd, offset, toSend, NULL, &r, 0)) { switch(errno) { case EAGAIN: - break; + case EINTR: + /* for EAGAIN/EINTR r still contains the sent bytes */ + break; /* try again later */ + case EPIPE: case ENOTCONN: - close(ifd); return -2; default: log_error_write(srv, __FILE__, __LINE__, "ssd", "sendfile: ", strerror(errno), errno); - close(ifd); return -1; } + } else if (r == 0) { + /* We got an event to write but we wrote nothing + * + * - the file shrinked -> error + * - the remote side closed inbetween -> remote-close */ + + if (HANDLER_ERROR == stat_cache_get_entry(srv, con, c->file.name, &sce)) { + /* file is gone ? */ + return -1; + } + + if (offset >= sce->st.st_size) { + /* file shrinked, close the connection */ + return -1; + } + + return -2; } - close(ifd); - + c->offset += r; cq->bytes_out += r; - + max_bytes -= r; + if (c->offset == c->file.length) { chunk_finished = 1; } - + break; } default: - + log_error_write(srv, __FILE__, __LINE__, "ds", c, "type not known"); - + return -1; } - + if (!chunk_finished) { /* not finished yet */ - + break; } } - return chunks_written; + return 0; } #endif diff --git a/src/network_linux_sendfile.c b/src/network_linux_sendfile.c index 5752385..9105603 100644 --- a/src/network_linux_sendfile.c +++ b/src/network_linux_sendfile.c @@ -1,6 +1,12 @@ #include "network_backends.h" #ifdef USE_LINUX_SENDFILE + +#include "network.h" +#include "fdevent.h" +#include "log.h" +#include "stat_cache.h" + #include <sys/types.h> #include <sys/socket.h> #include <sys/stat.h> @@ -16,41 +22,37 @@ #include <netdb.h> #include <string.h> #include <stdlib.h> +#include <fcntl.h> -#include "network.h" -#include "fdevent.h" -#include "log.h" -#include "stat_cache.h" +/* on linux 2.4.29 + debian/ubuntu we have crashes if this is enabled */ +#undef HAVE_POSIX_FADVISE -int network_write_chunkqueue_linuxsendfile(server *srv, connection *con, int fd, chunkqueue *cq) { +int network_write_chunkqueue_linuxsendfile(server *srv, connection *con, int fd, chunkqueue *cq, off_t max_bytes) { chunk *c; - size_t chunks_written = 0; - - for(c = cq->first; c; c = c->next, chunks_written++) { + + for(c = cq->first; (max_bytes > 0) && (NULL != c); c = c->next) { int chunk_finished = 0; - + switch(c->type) { case MEM_CHUNK: { char * offset; - size_t toSend; + off_t toSend; ssize_t r; - + size_t num_chunks, i; struct iovec chunks[UIO_MAXIOV]; chunk *tc; size_t num_bytes = 0; - - /* we can't send more then SSIZE_MAX bytes in one chunk */ - - /* build writev list - * + + /* build writev list + * * 1. limit: num_chunks < UIO_MAXIOV - * 2. limit: num_bytes < SSIZE_MAX + * 2. limit: num_bytes < max_bytes */ - for (num_chunks = 0, tc = c; - tc && tc->type == MEM_CHUNK && num_chunks < UIO_MAXIOV; + for (num_chunks = 0, tc = c; + tc && tc->type == MEM_CHUNK && num_chunks < UIO_MAXIOV; tc = tc->next, num_chunks++); - + for (tc = c, i = 0; i < num_chunks; tc = tc->next, i++) { if (tc->mem->used == 0) { chunks[i].iov_base = tc->mem->ptr; @@ -58,24 +60,24 @@ int network_write_chunkqueue_linuxsendfile(server *srv, connection *con, int fd, } else { offset = tc->mem->ptr + tc->offset; toSend = tc->mem->used - 1 - tc->offset; - + chunks[i].iov_base = offset; - + /* protect the return value of writev() */ - if (toSend > SSIZE_MAX || - num_bytes + toSend > SSIZE_MAX) { - chunks[i].iov_len = SSIZE_MAX - num_bytes; - + if (toSend > max_bytes || + (off_t) num_bytes + toSend > max_bytes) { + chunks[i].iov_len = max_bytes - num_bytes; + num_chunks = i + 1; break; } else { chunks[i].iov_len = toSend; } - + num_bytes += toSend; } } - + if ((r = writev(fd, chunks, num_chunks)) < 0) { switch (errno) { case EAGAIN: @@ -86,102 +88,131 @@ int network_write_chunkqueue_linuxsendfile(server *srv, connection *con, int fd, case ECONNRESET: return -2; default: - log_error_write(srv, __FILE__, __LINE__, "ssd", + log_error_write(srv, __FILE__, __LINE__, "ssd", "writev failed:", strerror(errno), fd); - + return -1; } } - + /* check which chunks have been written */ cq->bytes_out += r; + max_bytes -= r; for(i = 0, tc = c; i < num_chunks; i++, tc = tc->next) { if (r >= (ssize_t)chunks[i].iov_len) { /* written */ r -= chunks[i].iov_len; tc->offset += chunks[i].iov_len; - + if (chunk_finished) { /* skip the chunks from further touches */ - chunks_written++; c = c->next; } else { /* chunks_written + c = c->next is done in the for()*/ - chunk_finished++; + chunk_finished = 1; } } else { /* partially written */ - + tc->offset += r; chunk_finished = 0; - + break; } } - + break; } case FILE_CHUNK: { ssize_t r; off_t offset; - size_t toSend; + off_t toSend; stat_cache_entry *sce = NULL; - - if (HANDLER_ERROR == stat_cache_get_entry(srv, con, c->file.name, &sce)) { - log_error_write(srv, __FILE__, __LINE__, "sb", - strerror(errno), c->file.name); - return -1; - } - + offset = c->file.start + c->offset; - /* limit the toSend to 2^31-1 bytes in a chunk */ - toSend = c->file.length - c->offset > ((1 << 30) - 1) ? - ((1 << 30) - 1) : c->file.length - c->offset; - - if (offset > sce->st.st_size) { - log_error_write(srv, __FILE__, __LINE__, "sb", "file was shrinked:", c->file.name); - - return -1; - } - - /* open file if not already opened */ + toSend = c->file.length - c->offset; + if (toSend > max_bytes) toSend = max_bytes; + + /* open file if not already opened */ if (-1 == c->file.fd) { if (-1 == (c->file.fd = open(c->file.name->ptr, O_RDONLY))) { log_error_write(srv, __FILE__, __LINE__, "ss", "open failed: ", strerror(errno)); - + return -1; } #ifdef FD_CLOEXEC fcntl(c->file.fd, F_SETFD, FD_CLOEXEC); #endif +#ifdef HAVE_POSIX_FADVISE + /* tell the kernel that we want to stream the file */ + if (-1 == posix_fadvise(c->file.fd, 0, 0, POSIX_FADV_SEQUENTIAL)) { + if (ENOSYS != errno) { + log_error_write(srv, __FILE__, __LINE__, "ssd", + "posix_fadvise failed:", strerror(errno), c->file.fd); + } + } +#endif } - - /* Linux sendfile() */ + if (-1 == (r = sendfile(fd, c->file.fd, &offset, toSend))) { switch (errno) { case EAGAIN: case EINTR: + /* ok, we can't send more, let's try later again */ r = 0; break; case EPIPE: case ECONNRESET: return -2; default: - log_error_write(srv, __FILE__, __LINE__, "ssd", + log_error_write(srv, __FILE__, __LINE__, "ssd", "sendfile failed:", strerror(errno), fd); return -1; } - } + } else if (r == 0) { + int oerrno = errno; + /* We got an event to write but we wrote nothing + * + * - the file shrinked -> error + * - the remote side closed inbetween -> remote-close */ + + if (HANDLER_ERROR == stat_cache_get_entry(srv, con, c->file.name, &sce)) { + /* file is gone ? */ + return -1; + } + + if (offset > sce->st.st_size) { + /* file shrinked, close the connection */ + errno = oerrno; + + return -1; + } - if (r == 0) { - /* we got a event to write put we couldn't. remote side closed ? */ + errno = oerrno; return -2; } - + +#ifdef HAVE_POSIX_FADVISE +#if 0 +#define K * 1024 +#define M * 1024 K +#define READ_AHEAD 4 M + /* check if we need a new chunk */ + if ((c->offset & ~(READ_AHEAD - 1)) != ((c->offset + r) & ~(READ_AHEAD - 1))) { + /* tell the kernel that we want to stream the file */ + if (-1 == posix_fadvise(c->file.fd, (c->offset + r) & ~(READ_AHEAD - 1), READ_AHEAD, POSIX_FADV_NOREUSE)) { + log_error_write(srv, __FILE__, __LINE__, "ssd", + "posix_fadvise failed:", strerror(errno), c->file.fd); + } + } +#endif +#endif + c->offset += r; cq->bytes_out += r; - + max_bytes -= r; + if (c->offset == c->file.length) { chunk_finished = 1; @@ -192,24 +223,24 @@ int network_write_chunkqueue_linuxsendfile(server *srv, connection *con, int fd, c->file.fd = -1; } } - + break; } default: - + log_error_write(srv, __FILE__, __LINE__, "ds", c, "type not known"); - + return -1; } - + if (!chunk_finished) { /* not finished yet */ - + break; } } - return chunks_written; + return 0; } #endif diff --git a/src/network_openssl.c b/src/network_openssl.c index b6a1b2f..7bed710 100644 --- a/src/network_openssl.c +++ b/src/network_openssl.c @@ -1,6 +1,12 @@ #include "network_backends.h" #ifdef USE_OPENSSL + +#include "network.h" +#include "fdevent.h" +#include "log.h" +#include "stat_cache.h" + #include <sys/types.h> #include <sys/socket.h> #include <sys/stat.h> @@ -18,22 +24,16 @@ #include <stdlib.h> #include <assert.h> -#include "network.h" -#include "fdevent.h" -#include "log.h" -#include "stat_cache.h" - -# include <openssl/ssl.h> -# include <openssl/err.h> +# include <openssl/ssl.h> +# include <openssl/err.h> -int network_write_chunkqueue_openssl(server *srv, connection *con, SSL *ssl, chunkqueue *cq) { +int network_write_chunkqueue_openssl(server *srv, connection *con, SSL *ssl, chunkqueue *cq, off_t max_bytes) { int ssl_r; chunk *c; - size_t chunks_written = 0; /* this is a 64k sendbuffer * - * it has to stay at the same location all the time to satisfy the needs + * it has to stay at the same location all the time to satisfy the needs * of SSL_write to pass the SAME parameter in case of a _WANT_WRITE * * the buffer is allocated once, is NOT realloced and is NOT freed at shutdown @@ -43,14 +43,14 @@ int network_write_chunkqueue_openssl(server *srv, connection *con, SSL *ssl, chu * In reality we would like to use mmap() but we don't have a guarantee that * we get the same mmap() address for each call. On openbsd the mmap() address * even randomized. - * That means either we keep the mmap() open or we do a read() into a - * constant buffer + * That means either we keep the mmap() open or we do a read() into a + * constant buffer * */ #define LOCAL_SEND_BUFSIZE (64 * 1024) static char *local_send_buffer = NULL; /* the remote side closed the connection before without shutdown request - * - IE + * - IE * - wget * if keep-alive is disabled */ @@ -58,34 +58,43 @@ int network_write_chunkqueue_openssl(server *srv, connection *con, SSL *ssl, chu SSL_set_shutdown(ssl, SSL_RECEIVED_SHUTDOWN); } - for(c = cq->first; c; c = c->next) { + for(c = cq->first; (max_bytes > 0) && (NULL != c); c = c->next) { int chunk_finished = 0; - + switch(c->type) { case MEM_CHUNK: { char * offset; - size_t toSend; + off_t toSend; ssize_t r; - - if (c->mem->used == 0) { + + if (c->mem->used == 0 || c->mem->used == 1) { chunk_finished = 1; break; } - + offset = c->mem->ptr + c->offset; toSend = c->mem->used - 1 - c->offset; - + if (toSend > max_bytes) toSend = max_bytes; + /** * SSL_write man-page - * + * * WARNING * When an SSL_write() operation has to be repeated because of * SSL_ERROR_WANT_READ or SSL_ERROR_WANT_WRITE, it must be * repeated with the same arguments. - * + * */ - - if ((r = SSL_write(ssl, offset, toSend)) <= 0) { + + ERR_clear_error(); + r = SSL_write(ssl, offset, toSend); + + if (con->renegotiations > 1 && con->conf.ssl_disable_client_renegotiation) { + log_error_write(srv, __FILE__, __LINE__, "s", "SSL: renegotiation initiated by client"); + return -1; + } + + if (r <= 0) { unsigned long err; switch ((ssl_r = SSL_get_error(ssl, r))) { @@ -95,7 +104,7 @@ int network_write_chunkqueue_openssl(server *srv, connection *con, SSL *ssl, chu /* perhaps we have error waiting in our error-queue */ if (0 != (err = ERR_get_error())) { do { - log_error_write(srv, __FILE__, __LINE__, "sdds", "SSL:", + log_error_write(srv, __FILE__, __LINE__, "sdds", "SSL:", ssl_r, r, ERR_error_string(err, NULL)); } while((err = ERR_get_error())); @@ -103,45 +112,47 @@ int network_write_chunkqueue_openssl(server *srv, connection *con, SSL *ssl, chu /* no, but we have errno */ switch(errno) { case EPIPE: + case ECONNRESET: return -2; default: - log_error_write(srv, __FILE__, __LINE__, "sddds", "SSL:", + log_error_write(srv, __FILE__, __LINE__, "sddds", "SSL:", ssl_r, r, errno, strerror(errno)); break; } } else { /* neither error-queue nor errno ? */ - log_error_write(srv, __FILE__, __LINE__, "sddds", "SSL (error):", + log_error_write(srv, __FILE__, __LINE__, "sddds", "SSL (error):", ssl_r, r, errno, strerror(errno)); } - + return -1; case SSL_ERROR_ZERO_RETURN: /* clean shutdown on the remote side */ - + if (r == 0) return -2; - + /* fall through */ default: while((err = ERR_get_error())) { - log_error_write(srv, __FILE__, __LINE__, "sdds", "SSL:", + log_error_write(srv, __FILE__, __LINE__, "sdds", "SSL:", ssl_r, r, ERR_error_string(err, NULL)); } - + return -1; } } else { c->offset += r; cq->bytes_out += r; + max_bytes -= r; } - + if (c->offset == (off_t)c->mem->used - 1) { chunk_finished = 1; } - + break; } case FILE_CHUNK: { @@ -150,7 +161,7 @@ int network_write_chunkqueue_openssl(server *srv, connection *con, SSL *ssl, chu stat_cache_entry *sce = NULL; int ifd; int write_wait = 0; - + if (HANDLER_ERROR == stat_cache_get_entry(srv, con, c->file.name, &sce)) { log_error_write(srv, __FILE__, __LINE__, "sb", strerror(errno), c->file.name); @@ -164,13 +175,14 @@ int network_write_chunkqueue_openssl(server *srv, connection *con, SSL *ssl, chu do { off_t offset = c->file.start + c->offset; - off_t toSend = c->file.length - c->offset; + off_t toSend = c->file.length - c->offset; + if (toSend > max_bytes) toSend = max_bytes; if (toSend > LOCAL_SEND_BUFSIZE) toSend = LOCAL_SEND_BUFSIZE; - + if (-1 == (ifd = open(c->file.name->ptr, O_RDONLY))) { log_error_write(srv, __FILE__, __LINE__, "ss", "open failed:", strerror(errno)); - + return -1; } @@ -183,10 +195,18 @@ int network_write_chunkqueue_openssl(server *srv, connection *con, SSL *ssl, chu } s = local_send_buffer; - + close(ifd); - - if ((r = SSL_write(ssl, s, toSend)) <= 0) { + + ERR_clear_error(); + r = SSL_write(ssl, s, toSend); + + if (con->renegotiations > 1 && con->conf.ssl_disable_client_renegotiation) { + log_error_write(srv, __FILE__, __LINE__, "s", "SSL: renegotiation initiated by client"); + return -1; + } + + if (r <= 0) { unsigned long err; switch ((ssl_r = SSL_get_error(ssl, r))) { @@ -197,7 +217,7 @@ int network_write_chunkqueue_openssl(server *srv, connection *con, SSL *ssl, chu /* perhaps we have error waiting in our error-queue */ if (0 != (err = ERR_get_error())) { do { - log_error_write(srv, __FILE__, __LINE__, "sdds", "SSL:", + log_error_write(srv, __FILE__, __LINE__, "sdds", "SSL:", ssl_r, r, ERR_error_string(err, NULL)); } while((err = ERR_get_error())); @@ -205,64 +225,64 @@ int network_write_chunkqueue_openssl(server *srv, connection *con, SSL *ssl, chu /* no, but we have errno */ switch(errno) { case EPIPE: + case ECONNRESET: return -2; default: - log_error_write(srv, __FILE__, __LINE__, "sddds", "SSL:", + log_error_write(srv, __FILE__, __LINE__, "sddds", "SSL:", ssl_r, r, errno, strerror(errno)); break; } } else { /* neither error-queue nor errno ? */ - log_error_write(srv, __FILE__, __LINE__, "sddds", "SSL (error):", + log_error_write(srv, __FILE__, __LINE__, "sddds", "SSL (error):", ssl_r, r, errno, strerror(errno)); } - + return -1; case SSL_ERROR_ZERO_RETURN: /* clean shutdown on the remote side */ - + if (r == 0) return -2; - + /* fall thourgh */ default: while((err = ERR_get_error())) { - log_error_write(srv, __FILE__, __LINE__, "sdds", "SSL:", + log_error_write(srv, __FILE__, __LINE__, "sdds", "SSL:", ssl_r, r, ERR_error_string(err, NULL)); } - + return -1; } } else { c->offset += r; cq->bytes_out += r; + max_bytes -= r; } - + if (c->offset == c->file.length) { chunk_finished = 1; } - } while(!chunk_finished && !write_wait); - + } while (!chunk_finished && !write_wait && max_bytes > 0); + break; } default: log_error_write(srv, __FILE__, __LINE__, "s", "type not known"); - + return -1; } - + if (!chunk_finished) { /* not finished yet */ - + break; } - - chunks_written++; } - return chunks_written; + return 0; } #endif diff --git a/src/network_solaris_sendfilev.c b/src/network_solaris_sendfilev.c index 0ab669f..2003200 100644 --- a/src/network_solaris_sendfilev.c +++ b/src/network_solaris_sendfilev.c @@ -2,6 +2,11 @@ #ifdef USE_SOLARIS_SENDFILEV +#include "network.h" +#include "fdevent.h" +#include "log.h" +#include "stat_cache.h" + #include <sys/types.h> #include <sys/socket.h> #include <sys/stat.h> @@ -19,53 +24,47 @@ #include <stdlib.h> #include <limits.h> -#include "network.h" -#include "fdevent.h" -#include "log.h" -#include "stat_cache.h" - #ifndef UIO_MAXIOV -#define UIO_MAXIOV IOV_MAX +# define UIO_MAXIOV IOV_MAX #endif /** - * a very simple sendfilev() interface for solaris which can be optimised a lot more + * a very simple sendfilev() interface for solaris which can be optimised a lot more * as solaris sendfilev() supports 'sending everythin in one syscall()' - * - * If you want such an interface and need the performance, just give me an account on - * a solaris box. + * + * If you want such an interface and need the performance, just give me an account on + * a solaris box. * - jan@kneschke.de */ -int network_write_chunkqueue_solarissendfilev(server *srv, connection *con, int fd, chunkqueue *cq) { +int network_write_chunkqueue_solarissendfilev(server *srv, connection *con, int fd, chunkqueue *cq, off_t max_bytes) { chunk *c; - size_t chunks_written = 0; - - for(c = cq->first; c; c = c->next, chunks_written++) { + + for(c = cq->first; (max_bytes > 0) && (NULL != c); c = c->next) { int chunk_finished = 0; - + switch(c->type) { case MEM_CHUNK: { char * offset; - size_t toSend; + off_t toSend; ssize_t r; - + size_t num_chunks, i; struct iovec chunks[UIO_MAXIOV]; chunk *tc; - + size_t num_bytes = 0; - + /* we can't send more then SSIZE_MAX bytes in one chunk */ - - /* build writev list - * + + /* build writev list + * * 1. limit: num_chunks < UIO_MAXIOV * 2. limit: num_bytes < SSIZE_MAX */ for(num_chunks = 0, tc = c; tc && tc->type == MEM_CHUNK && num_chunks < UIO_MAXIOV; num_chunks++, tc = tc->next); - + for(tc = c, i = 0; i < num_chunks; tc = tc->next, i++) { if (tc->mem->used == 0) { chunks[i].iov_base = tc->mem->ptr; @@ -73,24 +72,24 @@ int network_write_chunkqueue_solarissendfilev(server *srv, connection *con, int } else { offset = tc->mem->ptr + tc->offset; toSend = tc->mem->used - 1 - tc->offset; - + chunks[i].iov_base = offset; - + /* protect the return value of writev() */ - if (toSend > SSIZE_MAX || - num_bytes + toSend > SSIZE_MAX) { - chunks[i].iov_len = SSIZE_MAX - num_bytes; - + if (toSend > max_bytes || + (off_t) num_bytes + toSend > max_bytes) { + chunks[i].iov_len = max_bytes - num_bytes; + num_chunks = i + 1; break; } else { chunks[i].iov_len = toSend; } - + num_bytes += toSend; } } - + if ((r = writev(fd, chunks, num_chunks)) < 0) { switch (errno) { case EAGAIN: @@ -101,68 +100,68 @@ int network_write_chunkqueue_solarissendfilev(server *srv, connection *con, int case ECONNRESET: return -2; default: - log_error_write(srv, __FILE__, __LINE__, "ssd", + log_error_write(srv, __FILE__, __LINE__, "ssd", "writev failed:", strerror(errno), fd); - + return -1; } } - + /* check which chunks have been written */ cq->bytes_out += r; - + for(i = 0, tc = c; i < num_chunks; i++, tc = tc->next) { if (r >= (ssize_t)chunks[i].iov_len) { /* written */ r -= chunks[i].iov_len; tc->offset += chunks[i].iov_len; - + if (chunk_finished) { /* skip the chunks from further touches */ - chunks_written++; c = c->next; } else { /* chunks_written + c = c->next is done in the for()*/ - chunk_finished++; + chunk_finished = 1; } } else { /* partially written */ - + tc->offset += r; chunk_finished = 0; - + break; } } - + break; } case FILE_CHUNK: { ssize_t r; - off_t offset; - size_t toSend, written; + off_t offset, toSend; + size_t written; sendfilevec_t fvec; stat_cache_entry *sce = NULL; int ifd; - + if (HANDLER_ERROR == stat_cache_get_entry(srv, con, c->file.name, &sce)) { log_error_write(srv, __FILE__, __LINE__, "sb", strerror(errno), c->file.name); return -1; } - + offset = c->file.start + c->offset; toSend = c->file.length - c->offset; - + if (toSend > max_bytes) toSend = max_bytes; + if (offset > sce->st.st_size) { log_error_write(srv, __FILE__, __LINE__, "sb", "file was shrinked:", c->file.name); - + return -1; } if (-1 == (ifd = open(c->file.name->ptr, O_RDONLY))) { log_error_write(srv, __FILE__, __LINE__, "ss", "open failed: ", strerror(errno)); - + return -1; } @@ -170,44 +169,45 @@ int network_write_chunkqueue_solarissendfilev(server *srv, connection *con, int fvec.sfv_flag = 0; fvec.sfv_off = offset; fvec.sfv_len = toSend; - + /* Solaris sendfilev() */ if (-1 == (r = sendfilev(fd, &fvec, 1, &written))) { if (errno != EAGAIN) { log_error_write(srv, __FILE__, __LINE__, "ssd", "sendfile: ", strerror(errno), errno); - + close(ifd); return -1; } - + r = 0; } - + close(ifd); c->offset += written; cq->bytes_out += written; - + max_bytes -= written; + if (c->offset == c->file.length) { chunk_finished = 1; } - + break; } default: - + log_error_write(srv, __FILE__, __LINE__, "ds", c, "type not known"); - + return -1; } - + if (!chunk_finished) { /* not finished yet */ - + break; } } - return chunks_written; + return 0; } #endif diff --git a/src/network_write.c b/src/network_write.c index 90fc2ac..6aa6cfa 100644 --- a/src/network_write.c +++ b/src/network_write.c @@ -1,11 +1,4 @@ -#include <sys/types.h> -#include <sys/stat.h> -#include <sys/time.h> -#include <errno.h> -#include <fcntl.h> -#include <unistd.h> -#include <string.h> -#include <stdlib.h> +#include "network_backends.h" #include "network.h" #include "fdevent.h" @@ -14,57 +7,78 @@ #include "sys-socket.h" -#include "network_backends.h" +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/time.h> +#include <errno.h> +#include <fcntl.h> +#include <unistd.h> +#include <string.h> +#include <stdlib.h> #ifdef HAVE_SYS_FILIO_H # include <sys/filio.h> #endif #ifdef HAVE_SYS_RESOURCE_H -#include <sys/resource.h> +# include <sys/resource.h> #endif -int network_write_chunkqueue_write(server *srv, connection *con, int fd, chunkqueue *cq) { +int network_write_chunkqueue_write(server *srv, connection *con, int fd, chunkqueue *cq, off_t max_bytes) { chunk *c; - size_t chunks_written = 0; - - for(c = cq->first; c; c = c->next) { + + for(c = cq->first; (max_bytes > 0) && (NULL != c); c = c->next) { int chunk_finished = 0; - + switch(c->type) { case MEM_CHUNK: { char * offset; - size_t toSend; + off_t toSend; ssize_t r; - + if (c->mem->used == 0) { chunk_finished = 1; break; } - + offset = c->mem->ptr + c->offset; toSend = c->mem->used - 1 - c->offset; -#ifdef __WIN32 + if (toSend > max_bytes) toSend = max_bytes; + +#ifdef __WIN32 if ((r = send(fd, offset, toSend, 0)) < 0) { - log_error_write(srv, __FILE__, __LINE__, "ssd", "write failed: ", strerror(errno), fd); - + /* no error handling for windows... */ + log_error_write(srv, __FILE__, __LINE__, "ssd", "send failed: ", strerror(errno), fd); + return -1; } #else if ((r = write(fd, offset, toSend)) < 0) { - log_error_write(srv, __FILE__, __LINE__, "ssd", "write failed: ", strerror(errno), fd); - - return -1; + switch (errno) { + case EAGAIN: + case EINTR: + r = 0; + break; + case EPIPE: + case ECONNRESET: + return -2; + default: + log_error_write(srv, __FILE__, __LINE__, "ssd", + "write failed:", strerror(errno), fd); + + return -1; + } } #endif - + c->offset += r; cq->bytes_out += r; - + max_bytes -= r; + if (c->offset == (off_t)c->mem->used - 1) { chunk_finished = 1; } - + break; } case FILE_CHUNK: { @@ -73,92 +87,127 @@ int network_write_chunkqueue_write(server *srv, connection *con, int fd, chunkqu #endif ssize_t r; off_t offset; - size_t toSend; + off_t toSend; stat_cache_entry *sce = NULL; int ifd; - + if (HANDLER_ERROR == stat_cache_get_entry(srv, con, c->file.name, &sce)) { log_error_write(srv, __FILE__, __LINE__, "sb", strerror(errno), c->file.name); return -1; } - + offset = c->file.start + c->offset; toSend = c->file.length - c->offset; - + + if (toSend > max_bytes) toSend = max_bytes; + if (offset > sce->st.st_size) { log_error_write(srv, __FILE__, __LINE__, "sb", "file was shrinked:", c->file.name); - + return -1; } if (-1 == (ifd = open(c->file.name->ptr, O_RDONLY))) { log_error_write(srv, __FILE__, __LINE__, "ss", "open failed: ", strerror(errno)); - + return -1; } - -#if defined USE_MMAP + +#ifdef USE_MMAP if (MAP_FAILED == (p = mmap(0, sce->st.st_size, PROT_READ, MAP_SHARED, ifd, 0))) { log_error_write(srv, __FILE__, __LINE__, "ss", "mmap failed: ", strerror(errno)); close(ifd); - + return -1; } close(ifd); if ((r = write(fd, p + offset, toSend)) <= 0) { - log_error_write(srv, __FILE__, __LINE__, "ss", "write failed: ", strerror(errno)); - munmap(p, sce->st.st_size); - return -1; + switch (errno) { + case EAGAIN: + case EINTR: + r = 0; + break; + case EPIPE: + case ECONNRESET: + munmap(p, sce->st.st_size); + return -2; + default: + log_error_write(srv, __FILE__, __LINE__, "ssd", + "write failed:", strerror(errno), fd); + munmap(p, sce->st.st_size); + + return -1; + } } - + munmap(p, sce->st.st_size); -#else +#else /* USE_MMAP */ buffer_prepare_copy(srv->tmp_buf, toSend); - + lseek(ifd, offset, SEEK_SET); if (-1 == (toSend = read(ifd, srv->tmp_buf->ptr, toSend))) { log_error_write(srv, __FILE__, __LINE__, "ss", "read: ", strerror(errno)); close(ifd); - + return -1; } close(ifd); - if (-1 == (r = send(fd, srv->tmp_buf->ptr, toSend, 0))) { - log_error_write(srv, __FILE__, __LINE__, "ss", "write: ", strerror(errno)); - +#ifdef __WIN32 + if ((r = send(fd, srv->tmp_buf->ptr, toSend, 0)) < 0) { + /* no error handling for windows... */ + log_error_write(srv, __FILE__, __LINE__, "ssd", "send failed: ", strerror(errno), fd); + return -1; } -#endif +#else /* __WIN32 */ + if ((r = write(fd, srv->tmp_buf->ptr, toSend)) < 0) { + switch (errno) { + case EAGAIN: + case EINTR: + r = 0; + break; + case EPIPE: + case ECONNRESET: + return -2; + default: + log_error_write(srv, __FILE__, __LINE__, "ssd", + "write failed:", strerror(errno), fd); + + return -1; + } + } +#endif /* __WIN32 */ +#endif /* USE_MMAP */ + c->offset += r; cq->bytes_out += r; - + max_bytes -= r; + if (c->offset == c->file.length) { chunk_finished = 1; } - + break; } default: - + log_error_write(srv, __FILE__, __LINE__, "ds", c, "type not known"); - + return -1; } - + if (!chunk_finished) { /* not finished yet */ - + break; } - - chunks_written++; } - return chunks_written; + return 0; } #if 0 diff --git a/src/network_writev.c b/src/network_writev.c index fcd06c4..d21cc4f 100644 --- a/src/network_writev.c +++ b/src/network_writev.c @@ -2,6 +2,11 @@ #ifdef USE_WRITEV +#include "network.h" +#include "fdevent.h" +#include "log.h" +#include "stat_cache.h" + #include <sys/types.h> #include <sys/socket.h> #include <sys/uio.h> @@ -21,63 +26,53 @@ #include <stdio.h> #include <assert.h> -#include "network.h" -#include "fdevent.h" -#include "log.h" -#include "stat_cache.h" - -#ifndef UIO_MAXIOV -# if defined(__FreeBSD__) || defined(__APPLE__) || defined(__NetBSD__) -/* FreeBSD 4.7 defines it in sys/uio.h only if _KERNEL is specified */ -# define UIO_MAXIOV 1024 -# elif defined(__sgi) -/* IRIX 6.5 has sysconf(_SC_IOV_MAX) which might return 512 or bigger */ -# define UIO_MAXIOV 512 -# elif defined(__sun) -/* Solaris (and SunOS?) defines IOV_MAX instead */ -# ifndef IOV_MAX -# define UIO_MAXIOV 16 -# else -# define UIO_MAXIOV IOV_MAX -# endif -# elif defined(IOV_MAX) -# define UIO_MAXIOV IOV_MAX -# else -# error UIO_MAXIOV nor IOV_MAX are defined -# endif -#endif - #if 0 #define LOCAL_BUFFERING 1 #endif -int network_write_chunkqueue_writev(server *srv, connection *con, int fd, chunkqueue *cq) { +int network_write_chunkqueue_writev(server *srv, connection *con, int fd, chunkqueue *cq, off_t max_bytes) { chunk *c; - size_t chunks_written = 0; - - for(c = cq->first; c; c = c->next) { + + for(c = cq->first; (max_bytes > 0) && (NULL != c); c = c->next) { int chunk_finished = 0; - + switch(c->type) { case MEM_CHUNK: { char * offset; - size_t toSend; + off_t toSend; ssize_t r; - + size_t num_chunks, i; - struct iovec chunks[UIO_MAXIOV]; + struct iovec *chunks; chunk *tc; size_t num_bytes = 0; - - /* we can't send more then SSIZE_MAX bytes in one chunk */ - - /* build writev list - * - * 1. limit: num_chunks < UIO_MAXIOV - * 2. limit: num_bytes < SSIZE_MAX +#if defined(_SC_IOV_MAX) /* IRIX, MacOS X, FreeBSD, Solaris, ... */ + const size_t max_chunks = sysconf(_SC_IOV_MAX); +#elif defined(IOV_MAX) /* Linux x86 (glibc-2.3.6-3) */ + const size_t max_chunks = IOV_MAX; +#elif defined(MAX_IOVEC) /* Linux ia64 (glibc-2.3.3-98.28) */ + const size_t max_chunks = MAX_IOVEC; +#elif defined(UIO_MAXIOV) /* Linux x86 (glibc-2.2.5-233) */ + const size_t max_chunks = UIO_MAXIOV; +#elif (defined(__FreeBSD__) && __FreeBSD_version < 500000) || defined(__DragonFly__) || defined(__APPLE__) + /* - FreeBSD 4.x + * - MacOS X 10.3.x + * (covered in -DKERNEL) + * */ + const size_t max_chunks = 1024; /* UIO_MAXIOV value from sys/uio.h */ +#else +#error "sysconf() doesnt return _SC_IOV_MAX ..., check the output of 'man writev' for the EINVAL error and send the output to jan@kneschke.de" +#endif + + /* build writev list + * + * 1. limit: num_chunks < max_chunks + * 2. limit: num_bytes < max_bytes */ - for(num_chunks = 0, tc = c; tc && tc->type == MEM_CHUNK && num_chunks < UIO_MAXIOV; num_chunks++, tc = tc->next); - + for (num_chunks = 0, tc = c; tc && tc->type == MEM_CHUNK && num_chunks < max_chunks; num_chunks++, tc = tc->next); + + chunks = calloc(num_chunks, sizeof(*chunks)); + for(tc = c, i = 0; i < num_chunks; tc = tc->next, i++) { if (tc->mem->used == 0) { chunks[i].iov_base = tc->mem->ptr; @@ -85,24 +80,24 @@ int network_write_chunkqueue_writev(server *srv, connection *con, int fd, chunkq } else { offset = tc->mem->ptr + tc->offset; toSend = tc->mem->used - 1 - tc->offset; - + chunks[i].iov_base = offset; - + /* protect the return value of writev() */ - if (toSend > SSIZE_MAX || - num_bytes + toSend > SSIZE_MAX) { - chunks[i].iov_len = SSIZE_MAX - num_bytes; - + if (toSend > max_bytes || + (off_t) num_bytes + toSend > max_bytes) { + chunks[i].iov_len = max_bytes - num_bytes; + num_chunks = i + 1; break; } else { chunks[i].iov_len = toSend; } - + num_bytes += toSend; } } - + if ((r = writev(fd, chunks, num_chunks)) < 0) { switch (errno) { case EAGAIN: @@ -111,43 +106,46 @@ int network_write_chunkqueue_writev(server *srv, connection *con, int fd, chunkq break; case EPIPE: case ECONNRESET: + free(chunks); return -2; default: - log_error_write(srv, __FILE__, __LINE__, "ssd", + log_error_write(srv, __FILE__, __LINE__, "ssd", "writev failed:", strerror(errno), fd); - + + free(chunks); return -1; } } - + cq->bytes_out += r; + max_bytes -= r; /* check which chunks have been written */ - + for(i = 0, tc = c; i < num_chunks; i++, tc = tc->next) { if (r >= (ssize_t)chunks[i].iov_len) { /* written */ r -= chunks[i].iov_len; tc->offset += chunks[i].iov_len; - + if (chunk_finished) { /* skip the chunks from further touches */ - chunks_written++; c = c->next; } else { /* chunks_written + c = c->next is done in the for()*/ - chunk_finished++; + chunk_finished = 1; } } else { /* partially written */ - + tc->offset += r; chunk_finished = 0; break; } } - + free(chunks); + break; } case FILE_CHUNK: { @@ -159,7 +157,7 @@ int network_write_chunkqueue_writev(server *srv, connection *con, int fd, chunkq #define KByte * 1024 #define MByte * 1024 KByte #define GByte * 1024 MByte - const off_t we_want_to_mmap = 512 KByte; + const off_t we_want_to_mmap = 512 KByte; char *start = NULL; if (HANDLER_ERROR == stat_cache_get_entry(srv, con, c->file.name, &sce)) { @@ -169,16 +167,16 @@ int network_write_chunkqueue_writev(server *srv, connection *con, int fd, chunkq } abs_offset = c->file.start + c->offset; - + if (abs_offset > sce->st.st_size) { - log_error_write(srv, __FILE__, __LINE__, "sb", + log_error_write(srv, __FILE__, __LINE__, "sb", "file was shrinked:", c->file.name); - + return -1; } - /* mmap the buffer - * - first mmap + /* mmap the buffer + * - first mmap * - new mmap as the we are at the end of the last one */ if (c->file.mmap.start == MAP_FAILED || abs_offset == (off_t)(c->file.mmap.offset + c->file.mmap.length)) { @@ -188,7 +186,7 @@ int network_write_chunkqueue_writev(server *srv, connection *con, int fd, chunkq * adaptive mem-mapping * the problem: * we mmap() the whole file. If someone has alot large files and 32bit - * machine the virtual address area will be unrun and we will have a failing + * machine the virtual address area will be unrun and we will have a failing * mmap() call. * solution: * only mmap 16M in one chunk and move the window as soon as we have finished @@ -234,7 +232,7 @@ int network_write_chunkqueue_writev(server *srv, connection *con, int fd, chunkq if (-1 == c->file.fd) { /* open the file if not already open */ if (-1 == (c->file.fd = open(c->file.name->ptr, O_RDONLY))) { log_error_write(srv, __FILE__, __LINE__, "sbs", "open failed for:", c->file.name, strerror(errno)); - + return -1; } #ifdef FD_CLOEXEC @@ -242,10 +240,10 @@ int network_write_chunkqueue_writev(server *srv, connection *con, int fd, chunkq #endif } - if (MAP_FAILED == (c->file.mmap.start = mmap(0, to_mmap, PROT_READ, MAP_SHARED, c->file.fd, c->file.mmap.offset))) { + if (MAP_FAILED == (c->file.mmap.start = mmap(NULL, to_mmap, PROT_READ, MAP_SHARED, c->file.fd, c->file.mmap.offset))) { /* close it here, otherwise we'd have to set FD_CLOEXEC */ - log_error_write(srv, __FILE__, __LINE__, "ssbd", "mmap failed:", + log_error_write(srv, __FILE__, __LINE__, "ssbd", "mmap failed:", strerror(errno), c->file.name, c->file.fd); return -1; @@ -257,10 +255,12 @@ int network_write_chunkqueue_writev(server *srv, connection *con, int fd, chunkq #else #ifdef HAVE_MADVISE /* don't advise files < 64Kb */ - if (c->file.mmap.length > (64 KByte) && - 0 != madvise(c->file.mmap.start, c->file.mmap.length, MADV_WILLNEED)) { - log_error_write(srv, __FILE__, __LINE__, "ssbd", "madvise failed:", - strerror(errno), c->file.name, c->file.fd); + if (c->file.mmap.length > (64 KByte)) { + /* darwin 7 is returning EINVAL all the time and I don't know how to + * detect this at runtime.i + * + * ignore the return value for now */ + madvise(c->file.mmap.start, c->file.mmap.length, MADV_WILLNEED); } #endif #endif @@ -272,15 +272,17 @@ int network_write_chunkqueue_writev(server *srv, connection *con, int fd, chunkq toSend = (c->file.mmap.offset + c->file.mmap.length) - (abs_offset); if (toSend < 0) { - log_error_write(srv, __FILE__, __LINE__, "soooo", + log_error_write(srv, __FILE__, __LINE__, "soooo", "toSend is negative:", toSend, c->file.mmap.length, abs_offset, - c->file.mmap.offset); + c->file.mmap.offset); assert(toSend < 0); } + if (toSend > max_bytes) toSend = max_bytes; + #ifdef LOCAL_BUFFERING start = c->mem->ptr; #else @@ -297,16 +299,17 @@ int network_write_chunkqueue_writev(server *srv, connection *con, int fd, chunkq case ECONNRESET: return -2; default: - log_error_write(srv, __FILE__, __LINE__, "ssd", + log_error_write(srv, __FILE__, __LINE__, "ssd", "write failed:", strerror(errno), fd); - + return -1; } } - + c->offset += r; cq->bytes_out += r; - + max_bytes -= r; + if (c->offset == c->file.length) { chunk_finished = 1; @@ -316,26 +319,24 @@ int network_write_chunkqueue_writev(server *srv, connection *con, int fd, chunkq c->file.mmap.start = MAP_FAILED; } } - + break; } default: - + log_error_write(srv, __FILE__, __LINE__, "ds", c, "type not known"); - + return -1; } - + if (!chunk_finished) { /* not finished yet */ - + break; } - - chunks_written++; } - return chunks_written; + return 0; } #endif diff --git a/src/plugin.c b/src/plugin.c index e74d8b0..55f8b03 100644 --- a/src/plugin.c +++ b/src/plugin.c @@ -1,39 +1,36 @@ +#include "plugin.h" +#include "log.h" + #include <string.h> #include <stdlib.h> #include <stdio.h> -#include "plugin.h" -#include "log.h" -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - #ifdef HAVE_VALGRIND_VALGRIND_H -#include <valgrind/valgrind.h> +# include <valgrind/valgrind.h> #endif #ifndef __WIN32 -#include <dlfcn.h> +# include <dlfcn.h> #endif /* - * + * * if you change this enum to add a new callback, be sure * - that PLUGIN_FUNC_SIZEOF is the last entry * - that you add PLUGIN_TO_SLOT twice: - * 1. as callback-dispatcher + * 1. as callback-dispatcher * 2. in plugins_call_init() - * + * */ typedef struct { PLUGIN_DATA; } plugin_data; -typedef enum { +typedef enum { PLUGIN_FUNC_UNSET, - PLUGIN_FUNC_HANDLE_URI_CLEAN, - PLUGIN_FUNC_HANDLE_URI_RAW, + PLUGIN_FUNC_HANDLE_URI_CLEAN, + PLUGIN_FUNC_HANDLE_URI_RAW, PLUGIN_FUNC_HANDLE_REQUEST_DONE, PLUGIN_FUNC_HANDLE_CONNECTION_CLOSE, PLUGIN_FUNC_HANDLE_TRIGGER, @@ -44,18 +41,18 @@ typedef enum { PLUGIN_FUNC_HANDLE_DOCROOT, PLUGIN_FUNC_HANDLE_PHYSICAL, PLUGIN_FUNC_CONNECTION_RESET, - PLUGIN_FUNC_INIT, + PLUGIN_FUNC_INIT, PLUGIN_FUNC_CLEANUP, PLUGIN_FUNC_SET_DEFAULTS, - + PLUGIN_FUNC_SIZEOF } plugin_t; static plugin *plugin_init(void) { plugin *p; - + p = calloc(1, sizeof(*p)); - + return p; } @@ -67,7 +64,7 @@ static void plugin_free(plugin *p) { #endif #ifndef LIGHTTPD_STATIC - if (use_dlclose && p->lib) { + if (use_dlclose && p->lib) { #ifdef __WIN32 FreeLibrary(p->lib); #else @@ -75,7 +72,7 @@ static void plugin_free(plugin *p) { #endif } #endif - + free(p); } @@ -89,17 +86,17 @@ static int plugins_register(server *srv, plugin *p) { srv->plugins.size += 4; srv->plugins.ptr = realloc(srv->plugins.ptr, srv->plugins.size * sizeof(*ps)); } - + ps = srv->plugins.ptr; ps[srv->plugins.used++] = p; - + return 0; } /** - * - * - * + * + * + * */ #ifdef LIGHTTPD_STATIC @@ -123,28 +120,35 @@ int plugins_load(server *srv) { plugin *p; int (*init)(plugin *pl); const char *error; - size_t i; - + size_t i, j; + for (i = 0; i < srv->srvconf.modules->used; i++) { data_string *d = (data_string *)srv->srvconf.modules->data[i]; char *modules = d->value->ptr; - + + for (j = 0; j < i; j++) { + if (buffer_is_equal(d->value, ((data_string *) srv->srvconf.modules->data[j])->value)) { + log_error_write(srv, __FILE__, __LINE__, "sbs", "Cannot load plugin", d->value, "more than once, please fix your config (we may not accept such configs in future releases"); + continue; + } + } + buffer_copy_string_buffer(srv->tmp_buf, srv->srvconf.modules_dir); - buffer_append_string(srv->tmp_buf, "/"); + buffer_append_string_len(srv->tmp_buf, CONST_STR_LEN("/")); buffer_append_string(srv->tmp_buf, modules); #if defined(__WIN32) || defined(__CYGWIN__) - buffer_append_string(srv->tmp_buf, ".dll"); + buffer_append_string_len(srv->tmp_buf, CONST_STR_LEN(".dll")); #else - buffer_append_string(srv->tmp_buf, ".so"); + buffer_append_string_len(srv->tmp_buf, CONST_STR_LEN(".so")); #endif - + p = plugin_init(); #ifdef __WIN32 if (NULL == (p->lib = LoadLibrary(srv->tmp_buf->ptr))) { LPVOID lpMsgBuf; FormatMessage( - FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(), @@ -152,28 +156,28 @@ int plugins_load(server *srv) { (LPTSTR) &lpMsgBuf, 0, NULL ); - log_error_write(srv, __FILE__, __LINE__, "ssb", "LoadLibrary() failed", + log_error_write(srv, __FILE__, __LINE__, "ssb", "LoadLibrary() failed", lpMsgBuf, srv->tmp_buf); - + plugin_free(p); - + return -1; } -#else - if (NULL == (p->lib = dlopen(srv->tmp_buf->ptr, RTLD_LAZY))) { - log_error_write(srv, __FILE__, __LINE__, "sbs", "dlopen() failed for:", +#else + if (NULL == (p->lib = dlopen(srv->tmp_buf->ptr, RTLD_NOW|RTLD_GLOBAL))) { + log_error_write(srv, __FILE__, __LINE__, "sbs", "dlopen() failed for:", srv->tmp_buf, dlerror()); - + plugin_free(p); - + return -1; } - + #endif buffer_reset(srv->tmp_buf); buffer_copy_string(srv->tmp_buf, modules); - buffer_append_string(srv->tmp_buf, "_plugin_init"); + buffer_append_string_len(srv->tmp_buf, CONST_STR_LEN("_plugin_init")); #ifdef __WIN32 init = GetProcAddress(p->lib, srv->tmp_buf->ptr); @@ -181,7 +185,7 @@ int plugins_load(server *srv) { if (init == NULL) { LPVOID lpMsgBuf; FormatMessage( - FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(), @@ -190,28 +194,28 @@ int plugins_load(server *srv) { 0, NULL ); log_error_write(srv, __FILE__, __LINE__, "sbs", "getprocaddress failed:", srv->tmp_buf, lpMsgBuf); - + plugin_free(p); return -1; } #else #if 1 - init = (int (*)(plugin *))dlsym(p->lib, srv->tmp_buf->ptr); + init = (int (*)(plugin *))(intptr_t)dlsym(p->lib, srv->tmp_buf->ptr); #else *(void **)(&init) = dlsym(p->lib, srv->tmp_buf->ptr); #endif if ((error = dlerror()) != NULL) { log_error_write(srv, __FILE__, __LINE__, "s", error); - + plugin_free(p); return -1; } - + #endif if ((*init)(p)) { log_error_write(srv, __FILE__, __LINE__, "ss", modules, "plugin init failed" ); - + plugin_free(p); return -1; } @@ -220,7 +224,7 @@ int plugins_load(server *srv) { #endif plugins_register(srv, p); } - + return 0; } #endif @@ -253,8 +257,8 @@ int plugins_load(server *srv) { } /** - * plugins that use - * + * plugins that use + * * - server *srv * - connection *con * - void *p_d (plugin_data *) @@ -301,12 +305,12 @@ PLUGIN_TO_SLOT(PLUGIN_FUNC_CONNECTION_RESET, connection_reset) } /** - * plugins that use - * + * plugins that use + * * - server *srv * - void *p_d (plugin_data *) */ - + PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_TRIGGER, handle_trigger) PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_SIGHUP, handle_sighup) PLUGIN_TO_SLOT(PLUGIN_FUNC_CLEANUP, cleanup) @@ -314,18 +318,18 @@ PLUGIN_TO_SLOT(PLUGIN_FUNC_SET_DEFAULTS, set_defaults) #undef PLUGIN_TO_SLOT -#if 0 +#if 0 /** - * + * * special handler - * + * */ handler_t plugins_call_handle_fdevent(server *srv, const fd_conn *fdc) { size_t i; plugin **ps; - + ps = srv->plugins.ptr; - + for (i = 0; i < srv->plugins.used; i++) { plugin *p = ps[i]; if (p->handle_fdevent) { @@ -344,34 +348,34 @@ handler_t plugins_call_handle_fdevent(server *srv, const fd_conn *fdc) { } } } - + return HANDLER_GO_ON; } #endif /** - * + * * - call init function of all plugins to init the plugin-internals * - added each plugin that supports has callback to the corresponding slot - * + * * - is only called once. */ handler_t plugins_call_init(server *srv) { size_t i; plugin **ps; - + ps = srv->plugins.ptr; - + /* fill slots */ - + srv->plugin_slots = calloc(PLUGIN_FUNC_SIZEOF, sizeof(ps)); - + for (i = 0; i < srv->plugins.used; i++) { size_t j; /* check which calls are supported */ - + plugin *p = ps[i]; - + #define PLUGIN_TO_SLOT(x, y) \ if (p->y) { \ plugin **slot = ((plugin ***)(srv->plugin_slots))[x]; \ @@ -384,11 +388,11 @@ handler_t plugins_call_init(server *srv) { slot[j] = p;\ break;\ }\ - } - - - PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_URI_CLEAN, handle_uri_clean); - PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_URI_RAW, handle_uri_raw); + } + + + PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_URI_CLEAN, handle_uri_clean); + PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_URI_RAW, handle_uri_raw); PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_REQUEST_DONE, handle_request_done); PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_CONNECTION_CLOSE, handle_connection_close); PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_TRIGGER, handle_trigger); @@ -402,19 +406,19 @@ handler_t plugins_call_init(server *srv) { PLUGIN_TO_SLOT(PLUGIN_FUNC_CLEANUP, cleanup); PLUGIN_TO_SLOT(PLUGIN_FUNC_SET_DEFAULTS, set_defaults); #undef PLUGIN_TO_SLOT - + if (p->init) { if (NULL == (p->data = p->init())) { - log_error_write(srv, __FILE__, __LINE__, "sb", + log_error_write(srv, __FILE__, __LINE__, "sb", "plugin-init failed for module", p->name); return HANDLER_ERROR; } - + /* used for con->mode, DIRECT == 0, plugins above that */ ((plugin_data *)(p->data))->id = i + 1; - + if (p->version != LIGHTTPD_VERSION_ID) { - log_error_write(srv, __FILE__, __LINE__, "sb", + log_error_write(srv, __FILE__, __LINE__, "sb", "plugin-version doesn't match lighttpd-version for", p->name); return HANDLER_ERROR; } @@ -422,29 +426,29 @@ handler_t plugins_call_init(server *srv) { p->data = NULL; } } - + return HANDLER_GO_ON; } void plugins_free(server *srv) { size_t i; plugins_call_cleanup(srv); - + for (i = 0; i < srv->plugins.used; i++) { plugin *p = ((plugin **)srv->plugins.ptr)[i]; - + plugin_free(p); } - + for (i = 0; srv->plugin_slots && i < PLUGIN_FUNC_SIZEOF; i++) { plugin **slot = ((plugin ***)(srv->plugin_slots))[i]; - + if (slot) free(slot); } - + free(srv->plugin_slots); srv->plugin_slots = NULL; - + free(srv->plugins.ptr); srv->plugins.ptr = NULL; srv->plugins.used = 0; diff --git a/src/plugin.h b/src/plugin.h index b43129a..cadce54 100644 --- a/src/plugin.h +++ b/src/plugin.h @@ -11,7 +11,7 @@ static handler_t x(server *srv, connection *con, void *p_d) #define INIT_FUNC(x) \ - static void *x() + static void *x(void) #define FREE_FUNC SERVER_FUNC #define TRIGGER_FUNC SERVER_FUNC @@ -25,19 +25,19 @@ #define URIHANDLER_FUNC CONNECTION_FUNC #define PLUGIN_DATA size_t id - + typedef struct { size_t version; - + buffer *name; /* name of the plugin */ - + void *(* init) (); handler_t (* set_defaults) (server *srv, void *p_d); handler_t (* cleanup) (server *srv, void *p_d); /* is called ... */ handler_t (* handle_trigger) (server *srv, void *p_d); /* once a second */ handler_t (* handle_sighup) (server *srv, void *p_d); /* at a signup */ - + handler_t (* handle_uri_raw) (server *srv, connection *con, void *p_d); /* after uri_raw is set */ handler_t (* handle_uri_clean) (server *srv, connection *con, void *p_d); /* after uri is set */ handler_t (* handle_docroot) (server *srv, connection *con, void *p_d); /* getting the document-root */ @@ -45,18 +45,18 @@ typedef struct { handler_t (* handle_request_done) (server *srv, connection *con, void *p_d); /* at the end of a request */ handler_t (* handle_connection_close)(server *srv, connection *con, void *p_d); /* at the end of a connection */ handler_t (* handle_joblist) (server *srv, connection *con, void *p_d); /* after all events are handled */ - - - - handler_t (* handle_subrequest_start)(server *srv, connection *con, void *p_d); - - /* when a handler for the request + + + + handler_t (* handle_subrequest_start)(server *srv, connection *con, void *p_d); + + /* when a handler for the request * has to be found */ handler_t (* handle_subrequest) (server *srv, connection *con, void *p_d); /* */ handler_t (* connection_reset) (server *srv, connection *con, void *p_d); /* */ void *data; - + /* dlopen handle */ void *lib; } plugin; diff --git a/src/proc_open.c b/src/proc_open.c index ecea815..e9393e0 100644 --- a/src/proc_open.c +++ b/src/proc_open.c @@ -1,15 +1,17 @@ +#include "proc_open.h" + #include <stdlib.h> #include <stdio.h> #include <ctype.h> +#include <string.h> #include <errno.h> -#include "proc_open.h" #ifdef WIN32 -#include <io.h> -#include <fcntl.h> +# include <io.h> +# include <fcntl.h> #else -#include <sys/wait.h> -#include <unistd.h> +# include <sys/wait.h> +# include <unistd.h> #endif @@ -148,11 +150,14 @@ int proc_open(proc_handler_t *proc, const char *command) { STARTUPINFO si; BOOL procok; SECURITY_ATTRIBUTES security; - const char *shell; + const char *shell = NULL; + const char *windir = NULL; buffer *cmdline; - if (NULL == (shell = getenv(SHELLENV))) { - fprintf(stderr, "env %s is required", SHELLENV); + if (NULL == (shell = getenv(SHELLENV)) && + NULL == (windir = getenv("SystemRoot")) && + NULL == (windir = getenv("windir"))) { + fprintf(stderr, "One of %s,%%SystemRoot,%%windir is required", SHELLENV); return -1; } @@ -177,17 +182,23 @@ int proc_open(proc_handler_t *proc, const char *command) { memset(&pi, 0, sizeof(pi)); cmdline = buffer_init(); - buffer_append_string(cmdline, shell); + if (shell) { + buffer_append_string(cmdline, shell); + } else { + buffer_append_string(cmdline, windir); + buffer_append_string_len(cmdline, CONST_STR_LEN("\\system32\\cmd.exe")); + } buffer_append_string_len(cmdline, CONST_STR_LEN(" /c ")); buffer_append_string(cmdline, command); procok = CreateProcess(NULL, cmdline->ptr, &security, &security, TRUE, NORMAL_PRIORITY_CLASS, NULL, NULL, &si, &pi); - buffer_free(cmdline); if (FALSE == procok) { - fprintf(stderr, "failed to CreateProcess"); + fprintf(stderr, "failed to CreateProcess: %s", cmdline->ptr); + buffer_free(cmdline); return -1; } + buffer_free(cmdline); proc->child = pi.hProcess; CloseHandle(pi.hThread); @@ -226,8 +237,7 @@ int proc_open(proc_handler_t *proc, const char *command) { const char *shell; if (NULL == (shell = getenv(SHELLENV))) { - fprintf(stderr, "env %s is required", SHELLENV); - return -1; + shell = "/bin/sh"; } if (proc_open_pipes(proc) != 0) { @@ -247,7 +257,8 @@ int proc_open(proc_handler_t *proc, const char *command) { */ proc_close_parents(proc); - execl(shell, shell, "-c", command, NULL); + execl(shell, shell, "-c", command, (char *)NULL); + fprintf(stderr, "failed to execute shell: %s -c %s: %s\n", shell, command, strerror(errno)); _exit(127); } else if (child < 0) { @@ -279,31 +290,40 @@ static void proc_read_fd_to_buffer(int fd, buffer *b) { } /* }}} */ /* {{{ proc_open_buffer */ -int proc_open_buffer(proc_handler_t *proc, const char *command, buffer *in, buffer *out, buffer *err) { - - UNUSED(err); +int proc_open_buffer(const char *command, buffer *in, buffer *out, buffer *err) { + proc_handler_t proc; - if (proc_open(proc, command) != 0) { + if (proc_open(&proc, command) != 0) { return -1; } if (in) { - if (write(proc->in.fd, (void *)in->ptr, in->used) < 0) { + if (write(proc.in.fd, (void *)in->ptr, in->used) < 0) { perror("error writing pipe"); return -1; } } - pipe_close(&proc->in); + pipe_close(&proc.in); if (out) { - proc_read_fd_to_buffer(proc->out.fd, out); + proc_read_fd_to_buffer(proc.out.fd, out); } - pipe_close(&proc->out); + pipe_close(&proc.out); if (err) { - proc_read_fd_to_buffer(proc->err.fd, err); + proc_read_fd_to_buffer(proc.err.fd, err); + } else { + buffer *tmp = buffer_init(); + proc_read_fd_to_buffer(proc.err.fd, tmp); + if (tmp->used > 0 && write(2, (void*)tmp->ptr, tmp->used) < 0) { + perror("error writing pipe"); + return -1; + } + buffer_free(tmp); } - pipe_close(&proc->err); + pipe_close(&proc.err); + + proc_close(&proc); return 0; } @@ -311,7 +331,7 @@ int proc_open_buffer(proc_handler_t *proc, const char *command, buffer *in, buff /* {{{ test */ #ifdef DEBUG_PROC_OPEN -int main() { +int main(void) { proc_handler_t proc; buffer *in = buffer_init(), *out = buffer_init(), *err = buffer_init(); int wstatus; @@ -358,7 +378,7 @@ int main() { RESET(); fprintf(stdout, "test: echo 321 with read\n"); fflush(stdout); - if (proc_open_buffer(&proc, "echo 321", NULL, out, err) != 0) { + if (proc_open_buffer("echo 321", NULL, out, err) != 0) { ERROR_OUT(); } fprintf(stdout, "result: ->%s<-\n\n", out->ptr); fflush(stdout); @@ -366,7 +386,7 @@ int main() { fprintf(stdout, "test: echo 123 | " CMD_CAT "\n"); fflush(stdout); buffer_copy_string_len(in, CONST_STR_LEN("123\n")); - if (proc_open_buffer(&proc, CMD_CAT, in, out, err) != 0) { + if (proc_open_buffer(CMD_CAT, in, out, err) != 0) { ERROR_OUT(); } fprintf(stdout, "result: ->%s<-\n\n", out->ptr); fflush(stdout); diff --git a/src/proc_open.h b/src/proc_open.h index e07421a..af70738 100644 --- a/src/proc_open.h +++ b/src/proc_open.h @@ -22,4 +22,4 @@ typedef struct { int proc_close(proc_handler_t *ht); int proc_open(proc_handler_t *ht, const char *command); -int proc_open_buffer(proc_handler_t *ht, const char *command, buffer *in, buffer *out, buffer *err); +int proc_open_buffer(const char *command, buffer *in, buffer *out, buffer *err); diff --git a/src/request.c b/src/request.c index db58671..8c6c170 100644 --- a/src/request.c +++ b/src/request.c @@ -1,3 +1,7 @@ +#include "request.h" +#include "keyvalue.h" +#include "log.h" + #include <sys/stat.h> #include <limits.h> @@ -6,19 +10,15 @@ #include <stdio.h> #include <ctype.h> -#include "request.h" -#include "keyvalue.h" -#include "log.h" - static int request_check_hostname(server *srv, connection *con, buffer *host) { enum { DOMAINLABEL, TOPLABEL } stage = TOPLABEL; size_t i; int label_len = 0; size_t host_len; char *colon; - int is_ip = -1; /* -1 don't know yet, 0 no, 1 yes */ + int is_ip = -1; /* -1 don't know yet, 0 no, 1 yes */ int level = 0; - + UNUSED(srv); UNUSED(con); @@ -32,33 +32,33 @@ static int request_check_hostname(server *srv, connection *con, buffer *host) { * IPv6address = "[" ... "]" * port = *digit */ - + /* no Host: */ if (!host || host->used == 0) return 0; - + host_len = host->used - 1; - + /* IPv6 adress */ if (host->ptr[0] == '[') { char *c = host->ptr + 1; int colon_cnt = 0; - + /* check portnumber */ for (; *c && *c != ']'; c++) { if (*c == ':') { if (++colon_cnt > 7) { return -1; } - } else if (!light_isxdigit(*c)) { + } else if (!light_isxdigit(*c) && '.' != *c) { return -1; } } - + /* missing ] */ if (!*c) { return -1; } - + /* check port */ if (*(c+1) == ':') { for (c += 2; *c; c++) { @@ -69,39 +69,50 @@ static int request_check_hostname(server *srv, connection *con, buffer *host) { } return 0; } - + if (NULL != (colon = memchr(host->ptr, ':', host_len))) { char *c = colon + 1; - + /* check portnumber */ for (; *c; c++) { if (!light_isdigit(*c)) return -1; } - + /* remove the port from the host-len */ host_len = colon - host->ptr; } - + /* Host is empty */ if (host_len == 0) return -1; - + + /* if the hostname ends in a "." strip it */ + if (host->ptr[host_len-1] == '.') { + /* shift port info one left */ + if (NULL != colon) memmove(colon-1, colon, host->used - host_len); + else host->ptr[host_len-1] = '\0'; + host_len -= 1; + host->used -= 1; + } + + if (host_len == 0) return -1; + /* scan from the right and skip the \0 */ - for (i = host_len - 1; i + 1 > 0; i--) { + for (i = host_len; i-- > 0; ) { const char c = host->ptr[i]; switch (stage) { - case TOPLABEL: + case TOPLABEL: if (c == '.') { /* only switch stage, if this is not the last character */ if (i != host_len - 1) { if (label_len == 0) { return -1; } - + /* check the first character at right of the dot */ if (is_ip == 0) { - if (!light_isalpha(host->ptr[i+1])) { - return -1; + if (!light_isalnum(host->ptr[i+1])) { + return -1; } } else if (!light_isdigit(host->ptr[i+1])) { is_ip = 0; @@ -111,9 +122,9 @@ static int request_check_hostname(server *srv, connection *con, buffer *host) { /* just digits */ is_ip = 1; } - + stage = DOMAINLABEL; - + label_len = 0; level++; } else if (i == 0) { @@ -122,7 +133,7 @@ static int request_check_hostname(server *srv, connection *con, buffer *host) { } } else if (i == 0) { /* the first character of the hostname */ - if (!light_isalpha(c)) { + if (!light_isalnum(c)) { return -1; } label_len++; @@ -135,7 +146,7 @@ static int request_check_hostname(server *srv, connection *con, buffer *host) { } label_len++; } - + break; case DOMAINLABEL: if (is_ip == 1) { @@ -143,7 +154,7 @@ static int request_check_hostname(server *srv, connection *con, buffer *host) { if (label_len == 0) { return -1; } - + label_len = 0; level++; } else if (!light_isdigit(c)) { @@ -156,12 +167,12 @@ static int request_check_hostname(server *srv, connection *con, buffer *host) { if (label_len == 0) { return -1; } - + /* c is either - or alphanum here */ if ('-' == host->ptr[i+1]) { return -1; } - + label_len = 0; level++; } else if (i == 0) { @@ -176,20 +187,20 @@ static int request_check_hostname(server *srv, connection *con, buffer *host) { label_len++; } } - + break; } } - + /* a IP has to consist of 4 parts */ if (is_ip == 1 && level != 3) { return -1; } - + if (label_len == 0) { return -1; } - + return 0; } @@ -197,73 +208,76 @@ static int request_check_hostname(server *srv, connection *con, buffer *host) { #define DUMP_HEADER #endif -int http_request_split_value(array *vals, buffer *b) { - char *s; +static int http_request_split_value(array *vals, buffer *b) { size_t i; int state = 0; - /* - * parse - * + + const char *current; + const char *token_start = NULL, *token_end = NULL; + /* + * parse + * * val1, val2, val3, val4 - * + * * into a array (more or less a explode() incl. striping of whitespaces */ - + if (b->used == 0) return 0; - - s = b->ptr; - - for (i =0; i < b->used - 1; ) { - char *start = NULL, *end = NULL; + + current = b->ptr; + for (i = 0; i < b->used; ++i, ++current) { data_string *ds; - + switch (state) { - case 0: /* ws */ - - /* skip ws */ - for (; (*s == ' ' || *s == '\t') && i < b->used - 1; i++, s++); - - - state = 1; - break; - case 1: /* value */ - start = s; - - for (; *s != ',' && i < b->used - 1; i++, s++); - end = s - 1; - - for (; (*end == ' ' || *end == '\t') && end > start; end--); - - if (NULL == (ds = (data_string *)array_get_unused_element(vals, TYPE_STRING))) { - ds = data_string_init(); + case 0: /* find start of a token */ + switch (*current) { + case ' ': + case '\t': /* skip white space */ + case ',': /* skip empty token */ + break; + case '\0': /* end of string */ + return 0; + default: + /* found real data, switch to state 1 to find the end of the token */ + token_start = token_end = current; + state = 1; + break; } + break; + case 1: /* find end of token and last non white space character */ + switch (*current) { + case ' ': + case '\t': + /* space - don't update token_end */ + break; + case ',': + case '\0': /* end of string also marks the end of a token */ + if (NULL == (ds = (data_string *)array_get_unused_element(vals, TYPE_STRING))) { + ds = data_string_init(); + } + + buffer_copy_string_len(ds->value, token_start, token_end-token_start+1); + array_insert_unique(vals, (data_unset *)ds); - buffer_copy_string_len(ds->value, start, end-start+1); - array_insert_unique(vals, (data_unset *)ds); - - if (*s == ',') { state = 0; - i++; - s++; - } else { - /* end of string */ - - state = 2; + break; + default: + /* no white space, update token_end to include current character */ + token_end = current; + break; } break; - default: - i++; - break; } } + return 0; } -int request_uri_is_valid_char(unsigned char c) { +static int request_uri_is_valid_char(unsigned char c) { if (c <= 32) return 0; if (c == 127) return 0; if (c == 255) return 0; - + return 1; } @@ -271,28 +285,28 @@ int http_request_parse(server *srv, connection *con) { char *uri = NULL, *proto = NULL, *method = NULL, con_length_set; int is_key = 1, key_len = 0, is_ws_after_key = 0, in_folding; char *value = NULL, *key = NULL; - + char *reqline_host = NULL; + int reqline_hostlen = 0; + enum { HTTP_CONNECTION_UNSET, HTTP_CONNECTION_KEEPALIVE, HTTP_CONNECTION_CLOSE } keep_alive_set = HTTP_CONNECTION_UNSET; - + int line = 0; - + int request_line_stage = 0; size_t i, first; - + int done = 0; - - data_string *ds = NULL; - - /* - * Request: "^(GET|POST|HEAD) ([^ ]+(\\?[^ ]+|)) (HTTP/1\\.[01])$" - * Option : "^([-a-zA-Z]+): (.+)$" + + /* + * Request: "^(GET|POST|HEAD) ([^ ]+(\\?[^ ]+|)) (HTTP/1\\.[01])$" + * Option : "^([-a-zA-Z]+): (.+)$" * End : "^$" */ if (con->conf.log_request_header) { - log_error_write(srv, __FILE__, __LINE__, "sdsdSb", - "fd:", con->fd, - "request-len:", con->request.request->used, + log_error_write(srv, __FILE__, __LINE__, "sdsdSb", + "fd:", con->fd, + "request-len:", con->request.request->used, "\n", con->request.request); } @@ -300,13 +314,13 @@ int http_request_parse(server *srv, connection *con) { con->request.request->ptr[0] == '\r' && con->request.request->ptr[1] == '\n') { /* we are in keep-alive and might get \r\n after a previous POST request.*/ - + buffer_copy_string_len(con->parse_request, con->request.request->ptr + 2, con->request.request->used - 1 - 2); } else { /* fill the local request buffer */ buffer_copy_string_buffer(con->parse_request, con->request.request); } - + keep_alive_set = 0; con_length_set = 0; @@ -318,67 +332,109 @@ int http_request_parse(server *srv, connection *con) { * */ for (i = 0, first = 0; i < con->parse_request->used && line == 0; i++) { char *cur = con->parse_request->ptr + i; - + switch(*cur) { - case '\r': + case '\r': if (con->parse_request->ptr[i+1] == '\n') { http_method_t r; char *nuri = NULL; size_t j; - + /* \r\n -> \0\0 */ con->parse_request->ptr[i] = '\0'; con->parse_request->ptr[i+1] = '\0'; - + buffer_copy_string_len(con->request.request_line, con->parse_request->ptr, i); - + if (request_line_stage != 2) { con->http_status = 400; con->response.keep_alive = 0; con->keep_alive = 0; - - log_error_write(srv, __FILE__, __LINE__, "s", "incomplete request line -> 400"); + if (srv->srvconf.log_request_header_on_error) { + log_error_write(srv, __FILE__, __LINE__, "s", "incomplete request line -> 400"); log_error_write(srv, __FILE__, __LINE__, "Sb", "request-header:\n", con->request.request); } return 0; } - + proto = con->parse_request->ptr + first; - + *(uri - 1) = '\0'; *(proto - 1) = '\0'; - + /* we got the first one :) */ if (-1 == (r = get_http_method_key(method))) { con->http_status = 501; con->response.keep_alive = 0; con->keep_alive = 0; - - log_error_write(srv, __FILE__, __LINE__, "s", "unknown http-method -> 501"); + if (srv->srvconf.log_request_header_on_error) { + log_error_write(srv, __FILE__, __LINE__, "s", "unknown http-method -> 501"); + log_error_write(srv, __FILE__, __LINE__, "Sb", + "request-header:\n", + con->request.request); + } + + return 0; + } + + con->request.http_method = r; + + /* + * RFC2616 says: + * + * HTTP-Version = "HTTP" "/" 1*DIGIT "." 1*DIGIT + * + * */ + if (0 == strncmp(proto, "HTTP/", sizeof("HTTP/") - 1)) { + char * major = proto + sizeof("HTTP/") - 1; + char * minor = strchr(major, '.'); + char *err = NULL; + int major_num = 0, minor_num = 0; + + int invalid_version = 0; + + if (NULL == minor || /* no dot */ + minor == major || /* no major */ + *(minor + 1) == '\0' /* no minor */) { + invalid_version = 1; + } else { + *minor = '\0'; + major_num = strtol(major, &err, 10); + + if (*err != '\0') invalid_version = 1; + + *minor++ = '.'; + minor_num = strtol(minor, &err, 10); + + if (*err != '\0') invalid_version = 1; + } + + if (invalid_version) { + con->http_status = 400; + con->keep_alive = 0; + + if (srv->srvconf.log_request_header_on_error) { + log_error_write(srv, __FILE__, __LINE__, "s", "unknown protocol -> 400"); log_error_write(srv, __FILE__, __LINE__, "Sb", "request-header:\n", con->request.request); } - - return 0; - } - - con->request.http_method = r; - - if (0 == strncmp(proto, "HTTP/1.", sizeof("HTTP/1.") - 1)) { - if (proto[7] == '1') { + return 0; + } + + if (major_num == 1 && minor_num == 1) { con->request.http_version = con->conf.allow_http11 ? HTTP_VERSION_1_1 : HTTP_VERSION_1_0; - } else if (proto[7] == '0') { + } else if (major_num == 1 && minor_num == 0) { con->request.http_version = HTTP_VERSION_1_0; - } else { + } else { con->http_status = 505; - log_error_write(srv, __FILE__, __LINE__, "s", "unknown HTTP version -> 505"); if (srv->srvconf.log_request_header_on_error) { + log_error_write(srv, __FILE__, __LINE__, "s", "unknown HTTP version -> 505"); log_error_write(srv, __FILE__, __LINE__, "Sb", "request-header:\n", con->request.request); @@ -389,62 +445,69 @@ int http_request_parse(server *srv, connection *con) { con->http_status = 400; con->keep_alive = 0; - log_error_write(srv, __FILE__, __LINE__, "s", "unknown protocol -> 400"); if (srv->srvconf.log_request_header_on_error) { - log_error_write(srv, __FILE__, __LINE__, "Sb", - "request-header:\n", - con->request.request); - } + log_error_write(srv, __FILE__, __LINE__, "s", "unknown protocol -> 400"); + log_error_write(srv, __FILE__, __LINE__, "Sb", + "request-header:\n", + con->request.request); + } return 0; } - + if (0 == strncmp(uri, "http://", 7) && NULL != (nuri = strchr(uri + 7, '/'))) { - /* ignore the host-part */ - + reqline_host = uri + 7; + reqline_hostlen = nuri - reqline_host; + + buffer_copy_string_len(con->request.uri, nuri, proto - nuri - 1); + } else if (0 == strncmp(uri, "https://", 8) && + NULL != (nuri = strchr(uri + 8, '/'))) { + reqline_host = uri + 8; + reqline_hostlen = nuri - reqline_host; + buffer_copy_string_len(con->request.uri, nuri, proto - nuri - 1); } else { /* everything looks good so far */ buffer_copy_string_len(con->request.uri, uri, proto - uri - 1); } - + /* check uri for invalid characters */ for (j = 0; j < con->request.uri->used - 1; j++) { if (!request_uri_is_valid_char(con->request.uri->ptr[j])) { unsigned char buf[2]; con->http_status = 400; con->keep_alive = 0; - - buf[0] = con->request.uri->ptr[j]; - buf[1] = '\0'; - - if (con->request.uri->ptr[j] > 32 && - con->request.uri->ptr[j] != 127) { - /* the character is printable -> print it */ - log_error_write(srv, __FILE__, __LINE__, "ss", - "invalid character in URI -> 400", - buf); - } else { - /* a control-character, print ascii-code */ - log_error_write(srv, __FILE__, __LINE__, "sd", - "invalid character in URI -> 400", - con->request.uri->ptr[j]); - } - + if (srv->srvconf.log_request_header_on_error) { + buf[0] = con->request.uri->ptr[j]; + buf[1] = '\0'; + + if (con->request.uri->ptr[j] > 32 && + con->request.uri->ptr[j] != 127) { + /* the character is printable -> print it */ + log_error_write(srv, __FILE__, __LINE__, "ss", + "invalid character in URI -> 400", + buf); + } else { + /* a control-character, print ascii-code */ + log_error_write(srv, __FILE__, __LINE__, "sd", + "invalid character in URI -> 400", + con->request.uri->ptr[j]); + } + log_error_write(srv, __FILE__, __LINE__, "Sb", "request-header:\n", con->request.request); } - + return 0; } } - + buffer_copy_string_buffer(con->request.orig_uri, con->request.uri); - + con->http_status = 0; - + i++; line++; first = i+1; @@ -452,14 +515,14 @@ int http_request_parse(server *srv, connection *con) { break; case ' ': switch(request_line_stage) { - case 0: + case 0: /* GET|POST|... */ - method = con->parse_request->ptr + first; + method = con->parse_request->ptr + first; first = i + 1; break; case 1: /* /foobar/... */ - uri = con->parse_request->ptr + first; + uri = con->parse_request->ptr + first; first = i + 1; break; default: @@ -467,21 +530,21 @@ int http_request_parse(server *srv, connection *con) { con->http_status = 400; con->response.keep_alive = 0; con->keep_alive = 0; - - log_error_write(srv, __FILE__, __LINE__, "s", "overlong request line -> 400"); + if (srv->srvconf.log_request_header_on_error) { + log_error_write(srv, __FILE__, __LINE__, "s", "overlong request line -> 400"); log_error_write(srv, __FILE__, __LINE__, "Sb", "request-header:\n", con->request.request); } return 0; } - + request_line_stage++; break; } } - + in_folding = 0; if (con->request.uri->used == 1) { @@ -489,8 +552,8 @@ int http_request_parse(server *srv, connection *con) { con->response.keep_alive = 0; con->keep_alive = 0; - log_error_write(srv, __FILE__, __LINE__, "s", "no uri specified -> 400"); if (srv->srvconf.log_request_header_on_error) { + log_error_write(srv, __FILE__, __LINE__, "s", "no uri specified -> 400"); log_error_write(srv, __FILE__, __LINE__, "Sb", "request-header:\n", con->request.request); @@ -498,30 +561,43 @@ int http_request_parse(server *srv, connection *con) { return 0; } - + if (reqline_host) { + /* Insert as host header */ + data_string *ds; + + if (NULL == (ds = (data_string *)array_get_unused_element(con->request.headers, TYPE_STRING))) { + ds = data_string_init(); + } + + buffer_copy_string_len(ds->key, CONST_STR_LEN("Host")); + buffer_copy_string_len(ds->value, reqline_host, reqline_hostlen); + array_insert_unique(con->request.headers, (data_unset *)ds); + con->request.http_host = ds->value; + } + for (; i < con->parse_request->used && !done; i++) { char *cur = con->parse_request->ptr + i; - + if (is_key) { size_t j; int got_colon = 0; - + /** * 1*<any CHAR except CTLs or separators> * CTLs == 0-31 + 127 - * + * */ switch(*cur) { case ':': is_key = 0; - + value = cur + 1; - + if (is_ws_after_key == 0) { key_len = i - first; } is_ws_after_key = 0; - + break; case '(': case ')': @@ -542,8 +618,8 @@ int http_request_parse(server *srv, connection *con) { con->http_status = 400; con->keep_alive = 0; con->response.keep_alive = 0; - - log_error_write(srv, __FILE__, __LINE__, "sbsds", + + log_error_write(srv, __FILE__, __LINE__, "sbsds", "invalid character in key", con->request.request, cur, *cur, "-> 400"); return 0; case ' ': @@ -552,13 +628,13 @@ int http_request_parse(server *srv, connection *con) { is_key = 0; in_folding = 1; value = cur; - + break; } - - + + key_len = i - first; - + /* skip every thing up to the : */ for (j = 1; !got_colon; j++) { switch(con->parse_request->ptr[j + i]) { @@ -567,40 +643,51 @@ int http_request_parse(server *srv, connection *con) { /* skip WS */ continue; case ':': - /* ok, done */ - + /* ok, done; handle the colon the usual way */ + i += j - 1; got_colon = 1; - + is_ws_after_key = 1; /* we already know the key length */ + break; default: /* error */ - - log_error_write(srv, __FILE__, __LINE__, "s", "WS character in key -> 400"); - + + if (srv->srvconf.log_request_header_on_error) { + log_error_write(srv, __FILE__, __LINE__, "s", "WS character in key -> 400"); + log_error_write(srv, __FILE__, __LINE__, "Sb", + "request-header:\n", + con->request.request); + } + con->http_status = 400; con->response.keep_alive = 0; con->keep_alive = 0; - + return 0; } } - + break; case '\r': if (con->parse_request->ptr[i+1] == '\n' && i == first) { /* End of Header */ con->parse_request->ptr[i] = '\0'; con->parse_request->ptr[i+1] = '\0'; - + i++; - + done = 1; - + break; } else { - log_error_write(srv, __FILE__, __LINE__, "s", "CR without LF -> 400"); - + if (srv->srvconf.log_request_header_on_error) { + log_error_write(srv, __FILE__, __LINE__, "s", "CR without LF -> 400"); + log_error_write(srv, __FILE__, __LINE__, "Sb", + "request-header:\n", + con->request.request); + } + con->http_status = 400; con->keep_alive = 0; con->response.keep_alive = 0; @@ -641,10 +728,16 @@ int http_request_parse(server *srv, connection *con) { con->http_status = 400; con->keep_alive = 0; con->response.keep_alive = 0; - - log_error_write(srv, __FILE__, __LINE__, "sbsds", + + if (srv->srvconf.log_request_header_on_error) { + log_error_write(srv, __FILE__, __LINE__, "sbsds", "CTL character in key", con->request.request, cur, *cur, "-> 400"); - + + log_error_write(srv, __FILE__, __LINE__, "Sb", + "request-header:\n", + con->request.request); + } + return 0; default: /* ok */ @@ -652,30 +745,64 @@ int http_request_parse(server *srv, connection *con) { } } else { switch(*cur) { - case '\r': + case '\r': if (con->parse_request->ptr[i+1] == '\n') { + data_string *ds = NULL; + /* End of Headerline */ con->parse_request->ptr[i] = '\0'; con->parse_request->ptr[i+1] = '\0'; - + if (in_folding) { - if (!ds) { + buffer *key_b; + /** + * we use a evil hack to handle the line-folding + * + * As array_insert_unique() deletes 'ds' in the case of a duplicate + * ds points somewhere and we get a evil crash. As a solution we keep the old + * "key" and get the current value from the hash and append us + * + * */ + + if (!key || !key_len) { /* 400 */ - - log_error_write(srv, __FILE__, __LINE__, "s", "WS at the start of first line -> 400"); - + + if (srv->srvconf.log_request_header_on_error) { + log_error_write(srv, __FILE__, __LINE__, "s", "WS at the start of first line -> 400"); + + log_error_write(srv, __FILE__, __LINE__, "Sb", + "request-header:\n", + con->request.request); + } + + con->http_status = 400; con->keep_alive = 0; con->response.keep_alive = 0; return 0; } - buffer_append_string(ds->value, value); + + key_b = buffer_init(); + buffer_copy_string_len(key_b, key, key_len); + + if (NULL != (ds = (data_string *)array_get_element(con->request.headers, key_b->ptr))) { + buffer_append_string(ds->value, value); + } + + buffer_free(key_b); } else { int s_len; key = con->parse_request->ptr + first; - + s_len = cur - value; - + + /* strip trailing white-spaces */ + for (; s_len > 0 && + (value[s_len - 1] == ' ' || + value[s_len - 1] == '\t'); s_len--); + + value[s_len] = '\0'; + if (s_len > 0) { int cmp = 0; if (NULL == (ds = (data_string *)array_get_unused_element(con->request.headers, TYPE_STRING))) { @@ -683,86 +810,87 @@ int http_request_parse(server *srv, connection *con) { } buffer_copy_string_len(ds->key, key, key_len); buffer_copy_string_len(ds->value, value, s_len); - - /* retreive values - * - * + + /* retreive values + * + * * the list of options is sorted to simplify the search */ - + if (0 == (cmp = buffer_caseless_compare(CONST_BUF_LEN(ds->key), CONST_STR_LEN("Connection")))) { array *vals; size_t vi; - + /* split on , */ - + vals = srv->split_vals; array_reset(vals); - + http_request_split_value(vals, ds->value); - + for (vi = 0; vi < vals->used; vi++) { data_string *dsv = (data_string *)vals->data[vi]; - + if (0 == buffer_caseless_compare(CONST_BUF_LEN(dsv->value), CONST_STR_LEN("keep-alive"))) { keep_alive_set = HTTP_CONNECTION_KEEPALIVE; - + break; } else if (0 == buffer_caseless_compare(CONST_BUF_LEN(dsv->value), CONST_STR_LEN("close"))) { keep_alive_set = HTTP_CONNECTION_CLOSE; - + break; } } - + } else if (cmp > 0 && 0 == (cmp = buffer_caseless_compare(CONST_BUF_LEN(ds->key), CONST_STR_LEN("Content-Length")))) { char *err; unsigned long int r; size_t j; - + if (con_length_set) { con->http_status = 400; con->keep_alive = 0; - - log_error_write(srv, __FILE__, __LINE__, "s", - "duplicate Content-Length-header -> 400"); + if (srv->srvconf.log_request_header_on_error) { + log_error_write(srv, __FILE__, __LINE__, "s", + "duplicate Content-Length-header -> 400"); log_error_write(srv, __FILE__, __LINE__, "Sb", "request-header:\n", con->request.request); } + array_insert_unique(con->request.headers, (data_unset *)ds); return 0; } - + if (ds->value->used == 0) SEGFAULT(); - + for (j = 0; j < ds->value->used - 1; j++) { char c = ds->value->ptr[j]; if (!isdigit((unsigned char)c)) { - log_error_write(srv, __FILE__, __LINE__, "sbs", + log_error_write(srv, __FILE__, __LINE__, "sbs", "content-length broken:", ds->value, "-> 400"); - + con->http_status = 400; con->keep_alive = 0; - + array_insert_unique(con->request.headers, (data_unset *)ds); return 0; } } - + r = strtoul(ds->value->ptr, &err, 10); - + if (*err == '\0') { con_length_set = 1; con->request.content_length = r; } else { - log_error_write(srv, __FILE__, __LINE__, "sbs", + log_error_write(srv, __FILE__, __LINE__, "sbs", "content-length broken:", ds->value, "-> 400"); - + con->http_status = 400; con->keep_alive = 0; - + array_insert_unique(con->request.headers, (data_unset *)ds); return 0; } @@ -773,23 +901,24 @@ int http_request_parse(server *srv, connection *con) { } else { con->http_status = 400; con->keep_alive = 0; - - log_error_write(srv, __FILE__, __LINE__, "s", - "duplicate Content-Type-header -> 400"); + if (srv->srvconf.log_request_header_on_error) { + log_error_write(srv, __FILE__, __LINE__, "s", + "duplicate Content-Type-header -> 400"); log_error_write(srv, __FILE__, __LINE__, "Sb", "request-header:\n", con->request.request); } + array_insert_unique(con->request.headers, (data_unset *)ds); return 0; } } else if (cmp > 0 && 0 == (cmp = buffer_caseless_compare(CONST_BUF_LEN(ds->key), CONST_STR_LEN("Expect")))) { - /* HTTP 2616 8.2.3 + /* HTTP 2616 8.2.3 * Expect: 100-continue - * + * * -> (10.1.1) 100 (read content, process request, send final status-code) * -> (10.4.18) 417 (close) - * + * * (not handled at all yet, we always send 417 here) * * What has to be added ? @@ -798,26 +927,32 @@ int http_request_parse(server *srv, connection *con) { * header * */ - - con->http_status = 417; - con->keep_alive = 0; - - array_insert_unique(con->request.headers, (data_unset *)ds); - return 0; + + if (srv->srvconf.reject_expect_100_with_417 && 0 == buffer_caseless_compare(CONST_BUF_LEN(ds->value), CONST_STR_LEN("100-continue"))) { + con->http_status = 417; + con->keep_alive = 0; + array_insert_unique(con->request.headers, (data_unset *)ds); + return 0; + } } else if (cmp > 0 && 0 == (cmp = buffer_caseless_compare(CONST_BUF_LEN(ds->key), CONST_STR_LEN("Host")))) { - if (!con->request.http_host) { + if (reqline_host) { + /* ignore all host: headers as we got the host in the request line */ + ds->free((data_unset*) ds); + ds = NULL; + } else if (!con->request.http_host) { con->request.http_host = ds->value; } else { con->http_status = 400; con->keep_alive = 0; - - log_error_write(srv, __FILE__, __LINE__, "s", - "duplicate Host-header -> 400"); + if (srv->srvconf.log_request_header_on_error) { + log_error_write(srv, __FILE__, __LINE__, "s", + "duplicate Host-header -> 400"); log_error_write(srv, __FILE__, __LINE__, "Sb", "request-header:\n", con->request.request); } + array_insert_unique(con->request.headers, (data_unset *)ds); return 0; } } else if (cmp > 0 && 0 == (cmp = buffer_caseless_compare(CONST_BUF_LEN(ds->key), CONST_STR_LEN("If-Modified-Since")))) { @@ -829,17 +964,21 @@ int http_request_parse(server *srv, connection *con) { } else if (0 == strcasecmp(con->request.http_if_modified_since, ds->value->ptr)) { /* ignore it if they are the same */ + + ds->free((data_unset *)ds); + ds = NULL; } else { con->http_status = 400; con->keep_alive = 0; - - log_error_write(srv, __FILE__, __LINE__, "s", - "duplicate If-Modified-Since header -> 400"); + if (srv->srvconf.log_request_header_on_error) { + log_error_write(srv, __FILE__, __LINE__, "s", + "duplicate If-Modified-Since header -> 400"); log_error_write(srv, __FILE__, __LINE__, "Sb", "request-header:\n", con->request.request); } + array_insert_unique(con->request.headers, (data_unset *)ds); return 0; } } else if (cmp > 0 && 0 == (cmp = buffer_caseless_compare(CONST_BUF_LEN(ds->key), CONST_STR_LEN("If-None-Match")))) { @@ -847,59 +986,58 @@ int http_request_parse(server *srv, connection *con) { if (!con->request.http_if_none_match) { con->request.http_if_none_match = ds->value->ptr; } else { - con->http_status = 400; - con->keep_alive = 0; - - log_error_write(srv, __FILE__, __LINE__, "s", - "duplicate If-None-Match-header -> 400"); - if (srv->srvconf.log_request_header_on_error) { - log_error_write(srv, __FILE__, __LINE__, "Sb", - "request-header:\n", - con->request.request); - } - return 0; + ds->free((data_unset*) ds); + ds = NULL; } } else if (cmp > 0 && 0 == (cmp = buffer_caseless_compare(CONST_BUF_LEN(ds->key), CONST_STR_LEN("Range")))) { if (!con->request.http_range) { /* bytes=.*-.* */ - + if (0 == strncasecmp(ds->value->ptr, "bytes=", 6) && NULL != strchr(ds->value->ptr+6, '-')) { - + /* if dup, only the first one will survive */ con->request.http_range = ds->value->ptr + 6; } } else { con->http_status = 400; con->keep_alive = 0; - - log_error_write(srv, __FILE__, __LINE__, "s", - "duplicate Host-header -> 400"); + if (srv->srvconf.log_request_header_on_error) { + log_error_write(srv, __FILE__, __LINE__, "s", + "duplicate Range-header -> 400"); log_error_write(srv, __FILE__, __LINE__, "Sb", "request-header:\n", con->request.request); } + array_insert_unique(con->request.headers, (data_unset *)ds); return 0; } } - - array_insert_unique(con->request.headers, (data_unset *)ds); + + if (ds) array_insert_unique(con->request.headers, (data_unset *)ds); } else { /* empty header-fields are not allowed by HTTP-RFC, we just ignore them */ } } - + i++; first = i+1; is_key = 1; - value = 0; - key_len = 0; + value = NULL; +#if 0 + /** + * for Bug 1230 keep the key_len a live + */ + key_len = 0; +#endif in_folding = 0; } else { - log_error_write(srv, __FILE__, __LINE__, "sbs", - "CR without LF", con->request.request, "-> 400"); - + if (srv->srvconf.log_request_header_on_error) { + log_error_write(srv, __FILE__, __LINE__, "sbs", + "CR without LF", con->request.request, "-> 400"); + } + con->http_status = 400; con->keep_alive = 0; con->response.keep_alive = 0; @@ -911,34 +1049,45 @@ int http_request_parse(server *srv, connection *con) { /* strip leading WS */ if (value == cur) value = cur+1; default: + if (*cur >= 0 && *cur < 32 && *cur != '\t') { + if (srv->srvconf.log_request_header_on_error) { + log_error_write(srv, __FILE__, __LINE__, "sds", + "invalid char in header", (int)*cur, "-> 400"); + } + + con->http_status = 400; + con->keep_alive = 0; + + return 0; + } break; } } } - + con->header_len = i; - + /* do some post-processing */ if (con->request.http_version == HTTP_VERSION_1_1) { if (keep_alive_set != HTTP_CONNECTION_CLOSE) { /* no Connection-Header sent */ - + /* HTTP/1.1 -> keep-alive default TRUE */ con->keep_alive = 1; } else { con->keep_alive = 0; } - + /* RFC 2616, 14.23 */ if (con->request.http_host == NULL || buffer_is_empty(con->request.http_host)) { con->http_status = 400; con->response.keep_alive = 0; con->keep_alive = 0; - - log_error_write(srv, __FILE__, __LINE__, "s", "HTTP/1.1 but Host missing -> 400"); + if (srv->srvconf.log_request_header_on_error) { + log_error_write(srv, __FILE__, __LINE__, "s", "HTTP/1.1 but Host missing -> 400"); log_error_write(srv, __FILE__, __LINE__, "Sb", "request-header:\n", con->request.request); @@ -948,21 +1097,21 @@ int http_request_parse(server *srv, connection *con) { } else { if (keep_alive_set == HTTP_CONNECTION_KEEPALIVE) { /* no Connection-Header sent */ - + /* HTTP/1.0 -> keep-alive default FALSE */ con->keep_alive = 1; } else { con->keep_alive = 0; } } - + /* check hostname field if it is set */ if (NULL != con->request.http_host && 0 != request_check_hostname(srv, con, con->request.http_host)) { - log_error_write(srv, __FILE__, __LINE__, "s", - "Invalid Hostname -> 400"); - + if (srv->srvconf.log_request_header_on_error) { + log_error_write(srv, __FILE__, __LINE__, "s", + "Invalid Hostname -> 400"); log_error_write(srv, __FILE__, __LINE__, "Sb", "request-header:\n", con->request.request); @@ -971,21 +1120,20 @@ int http_request_parse(server *srv, connection *con) { con->http_status = 400; con->response.keep_alive = 0; con->keep_alive = 0; - + return 0; } switch(con->request.http_method) { case HTTP_METHOD_GET: case HTTP_METHOD_HEAD: - case HTTP_METHOD_OPTIONS: /* content-length is forbidden for those */ if (con_length_set && con->request.content_length != 0) { /* content-length is missing */ - log_error_write(srv, __FILE__, __LINE__, "s", - "GET/HEAD/OPTIONS with content-length -> 400"); + log_error_write(srv, __FILE__, __LINE__, "s", + "GET/HEAD with content-length -> 400"); + con->keep_alive = 0; - con->http_status = 400; return 0; } @@ -994,10 +1142,10 @@ int http_request_parse(server *srv, connection *con) { /* content-length is required for them */ if (!con_length_set) { /* content-length is missing */ - log_error_write(srv, __FILE__, __LINE__, "s", + log_error_write(srv, __FILE__, __LINE__, "s", "POST-request, but content-length missing -> 411"); + con->keep_alive = 0; - con->http_status = 411; return 0; @@ -1007,42 +1155,42 @@ int http_request_parse(server *srv, connection *con) { /* the may have a content-length */ break; } - - + + /* check if we have read post data */ if (con_length_set) { /* don't handle more the SSIZE_MAX bytes in content-length */ if (con->request.content_length > SSIZE_MAX) { - con->http_status = 413; + con->http_status = 413; con->keep_alive = 0; - log_error_write(srv, __FILE__, __LINE__, "sds", - "request-size too long:", con->request.content_length, "-> 413"); + log_error_write(srv, __FILE__, __LINE__, "sos", + "request-size too long:", (off_t) con->request.content_length, "-> 413"); return 0; } /* divide by 1024 as srvconf.max_request_size is in kBytes */ if (srv->srvconf.max_request_size != 0 && (con->request.content_length >> 10) > srv->srvconf.max_request_size) { - /* the request body itself is larger then + /* the request body itself is larger then * our our max_request_size */ - + con->http_status = 413; con->keep_alive = 0; - - log_error_write(srv, __FILE__, __LINE__, "sds", - "request-size too long:", con->request.content_length, "-> 413"); + + log_error_write(srv, __FILE__, __LINE__, "sos", + "request-size too long:", (off_t) con->request.content_length, "-> 413"); return 0; } - - + + /* we have content */ if (con->request.content_length != 0) { return 1; } } - + return 0; } @@ -1050,9 +1198,9 @@ int http_request_header_finished(server *srv, connection *con) { UNUSED(srv); if (con->request.request->used < 5) return 0; - + if (0 == memcmp(con->request.request->ptr + con->request.request->used - 5, "\r\n\r\n", 4)) return 1; if (NULL != strstr(con->request.request->ptr, "\r\n\r\n")) return 1; - + return 0; } diff --git a/src/response.c b/src/response.c index d955cec..fd1ab19 100644 --- a/src/response.c +++ b/src/response.c @@ -1,3 +1,14 @@ +#include "response.h" +#include "keyvalue.h" +#include "log.h" +#include "stat_cache.h" +#include "chunk.h" + +#include "configfile.h" +#include "connections.h" + +#include "plugin.h" + #include <sys/types.h> #include <sys/stat.h> @@ -13,176 +24,269 @@ #include <stdio.h> -#include "response.h" -#include "keyvalue.h" -#include "log.h" -#include "stat_cache.h" -#include "chunk.h" - -#include "connections.h" - -#include "plugin.h" - #include "sys-socket.h" +#include "version.h" int http_response_write_header(server *srv, connection *con) { buffer *b; size_t i; int have_date = 0; int have_server = 0; - + b = chunkqueue_get_prepend_buffer(con->write_queue); - + if (con->request.http_version == HTTP_VERSION_1_1) { - BUFFER_COPY_STRING_CONST(b, "HTTP/1.1 "); + buffer_copy_string_len(b, CONST_STR_LEN("HTTP/1.1 ")); } else { - BUFFER_COPY_STRING_CONST(b, "HTTP/1.0 "); + buffer_copy_string_len(b, CONST_STR_LEN("HTTP/1.0 ")); } buffer_append_long(b, con->http_status); - BUFFER_APPEND_STRING_CONST(b, " "); + buffer_append_string_len(b, CONST_STR_LEN(" ")); buffer_append_string(b, get_http_status_name(con->http_status)); - + + /* disable keep-alive if requested */ + if (con->request_count > con->conf.max_keep_alive_requests || 0 == con->conf.max_keep_alive_idle) { + con->keep_alive = 0; + } else { + con->keep_alive_idle = con->conf.max_keep_alive_idle; + } + if (con->request.http_version != HTTP_VERSION_1_1 || con->keep_alive == 0) { - BUFFER_APPEND_STRING_CONST(b, "\r\nConnection: "); - buffer_append_string(b, con->keep_alive ? "keep-alive" : "close"); + if (con->keep_alive) { + response_header_overwrite(srv, con, CONST_STR_LEN("Connection"), CONST_STR_LEN("keep-alive")); + } else { + response_header_overwrite(srv, con, CONST_STR_LEN("Connection"), CONST_STR_LEN("close")); + } } - + if (con->response.transfer_encoding & HTTP_TRANSFER_ENCODING_CHUNKED) { - BUFFER_APPEND_STRING_CONST(b, "\r\nTransfer-Encoding: chunked"); + response_header_overwrite(srv, con, CONST_STR_LEN("Transfer-Encoding"), CONST_STR_LEN("chunked")); } - - + + /* add all headers */ for (i = 0; i < con->response.headers->used; i++) { data_string *ds; - + ds = (data_string *)con->response.headers->data[i]; - + if (ds->value->used && ds->key->used && - 0 != strncmp(ds->key->ptr, "X-LIGHTTPD-", sizeof("X-LIGHTTPD-") - 1)) { - if (buffer_is_equal_string(ds->key, CONST_STR_LEN("Date"))) have_date = 1; - if (buffer_is_equal_string(ds->key, CONST_STR_LEN("Server"))) have_server = 1; + 0 != strncasecmp(ds->key->ptr, CONST_STR_LEN("X-LIGHTTPD-")) && + 0 != strncasecmp(ds->key->ptr, CONST_STR_LEN("X-Sendfile"))) { + if (0 == strcasecmp(ds->key->ptr, "Date")) have_date = 1; + if (0 == strcasecmp(ds->key->ptr, "Server")) have_server = 1; + if (0 == strcasecmp(ds->key->ptr, "Content-Encoding") && 304 == con->http_status) continue; - BUFFER_APPEND_STRING_CONST(b, "\r\n"); + buffer_append_string_len(b, CONST_STR_LEN("\r\n")); buffer_append_string_buffer(b, ds->key); - BUFFER_APPEND_STRING_CONST(b, ": "); - buffer_append_string_buffer(b, ds->value); + buffer_append_string_len(b, CONST_STR_LEN(": ")); #if 0 - log_error_write(srv, __FILE__, __LINE__, "bb", - ds->key, ds->value); + /** + * the value might contain newlines, encode them with at least one white-space + */ + buffer_append_string_encoded(b, CONST_BUF_LEN(ds->value), ENCODING_HTTP_HEADER); +#else + buffer_append_string_buffer(b, ds->value); #endif } } - + if (!have_date) { /* HTTP/1.1 requires a Date: header */ - BUFFER_APPEND_STRING_CONST(b, "\r\nDate: "); - + buffer_append_string_len(b, CONST_STR_LEN("\r\nDate: ")); + /* cache the generated timestamp */ if (srv->cur_ts != srv->last_generated_date_ts) { buffer_prepare_copy(srv->ts_date_str, 255); - - strftime(srv->ts_date_str->ptr, srv->ts_date_str->size - 1, + + strftime(srv->ts_date_str->ptr, srv->ts_date_str->size - 1, "%a, %d %b %Y %H:%M:%S GMT", gmtime(&(srv->cur_ts))); - + srv->ts_date_str->used = strlen(srv->ts_date_str->ptr) + 1; - + srv->last_generated_date_ts = srv->cur_ts; } - + buffer_append_string_buffer(b, srv->ts_date_str); } if (!have_server) { if (buffer_is_empty(con->conf.server_tag)) { - BUFFER_APPEND_STRING_CONST(b, "\r\nServer: " PACKAGE_NAME "/" PACKAGE_VERSION); - } else { - BUFFER_APPEND_STRING_CONST(b, "\r\nServer: "); - buffer_append_string_buffer(b, con->conf.server_tag); + buffer_append_string_len(b, CONST_STR_LEN("\r\nServer: " PACKAGE_DESC)); + } else if (con->conf.server_tag->used > 1) { + buffer_append_string_len(b, CONST_STR_LEN("\r\nServer: ")); + buffer_append_string_encoded(b, CONST_BUF_LEN(con->conf.server_tag), ENCODING_HTTP_HEADER); } } - - BUFFER_APPEND_STRING_CONST(b, "\r\n\r\n"); - - + + buffer_append_string_len(b, CONST_STR_LEN("\r\n\r\n")); + + con->bytes_header = b->used - 1; - + if (con->conf.log_response_header) { log_error_write(srv, __FILE__, __LINE__, "sSb", "Response-Header:", "\n", b); } - + return 0; } +#ifdef USE_OPENSSL +static void https_add_ssl_entries(connection *con) { + X509 *xs; + X509_NAME *xn; + X509_NAME_ENTRY *xe; + int i, nentries; + + if ( + SSL_get_verify_result(con->ssl) != X509_V_OK + || !(xs = SSL_get_peer_certificate(con->ssl)) + ) { + return; + } + + xn = X509_get_subject_name(xs); + for (i = 0, nentries = X509_NAME_entry_count(xn); i < nentries; ++i) { + int xobjnid; + const char * xobjsn; + data_string *envds; + + if (!(xe = X509_NAME_get_entry(xn, i))) { + continue; + } + xobjnid = OBJ_obj2nid((ASN1_OBJECT*)X509_NAME_ENTRY_get_object(xe)); + xobjsn = OBJ_nid2sn(xobjnid); + if (!xobjsn) { + continue; + } + + if (NULL == (envds = (data_string *)array_get_unused_element(con->environment, TYPE_STRING))) { + envds = data_string_init(); + } + buffer_copy_string_len(envds->key, CONST_STR_LEN("SSL_CLIENT_S_DN_")); + buffer_append_string(envds->key, xobjsn); + buffer_copy_string_len( + envds->value, + (const char *)xe->value->data, xe->value->length + ); + /* pick one of the exported values as "authed user", for example + * ssl.verifyclient.username = "SSL_CLIENT_S_DN_UID" or "SSL_CLIENT_S_DN_emailAddress" + */ + if (buffer_is_equal(con->conf.ssl_verifyclient_username, envds->key)) { + buffer_copy_string_buffer(con->authed_user, envds->value); + } + array_insert_unique(con->environment, (data_unset *)envds); + } + if (con->conf.ssl_verifyclient_export_cert) { + BIO *bio; + if (NULL != (bio = BIO_new(BIO_s_mem()))) { + data_string *envds; + int n; + + PEM_write_bio_X509(bio, xs); + n = BIO_pending(bio); + + if (NULL == (envds = (data_string *)array_get_unused_element(con->environment, TYPE_STRING))) { + envds = data_string_init(); + } + + buffer_copy_string_len(envds->key, CONST_STR_LEN("SSL_CLIENT_CERT")); + buffer_prepare_copy(envds->value, n+1); + BIO_read(bio, envds->value->ptr, n); + BIO_free(bio); + envds->value->ptr[n] = '\0'; + envds->value->used = n+1; + array_insert_unique(con->environment, (data_unset *)envds); + } + } + X509_free(xs); +} +#endif handler_t http_response_prepare(server *srv, connection *con) { handler_t r; - + /* looks like someone has already done a decision */ - if (con->mode == DIRECT && + if (con->mode == DIRECT && (con->http_status != 0 && con->http_status != 200)) { /* remove a packets in the queue */ if (con->file_finished == 0) { chunkqueue_reset(con->write_queue); } - + return HANDLER_FINISHED; } - + /* no decision yet, build conf->filename */ if (con->mode == DIRECT && con->physical.path->used == 0) { char *qstr; /* we only come here when we have the parse the full request again - * - * a HANDLER_COMEBACK from mod_rewrite and mod_fastcgi might be a + * + * a HANDLER_COMEBACK from mod_rewrite and mod_fastcgi might be a * problem here as mod_setenv might get called multiple times * * fastcgi-auth might lead to a COMEBACK too * fastcgi again dead server too * * mod_compress might add headers twice too - * + * * */ - + + config_cond_cache_reset(srv, con); + config_setup_connection(srv, con); /* Perhaps this could be removed at other places. */ + if (con->conf.log_condition_handling) { log_error_write(srv, __FILE__, __LINE__, "s", "run condition"); } config_patch_connection(srv, con, COMP_SERVER_SOCKET); /* SERVERsocket */ - + /** * prepare strings - * - * - uri.path_raw + * + * - uri.path_raw * - uri.path (secure) * - uri.query - * + * */ - - /** + + /** * Name according to RFC 2396 - * + * * - scheme * - authority * - path * - query - * - * (scheme)://(authority)(path)?(query) - * - * + * + * (scheme)://(authority)(path)?(query)#fragment + * + * */ - - buffer_copy_string(con->uri.scheme, con->conf.is_ssl ? "https" : "http"); + + if (con->conf.is_ssl) { + buffer_copy_string_len(con->uri.scheme, CONST_STR_LEN("https")); + } else { + buffer_copy_string_len(con->uri.scheme, CONST_STR_LEN("http")); + } buffer_copy_string_buffer(con->uri.authority, con->request.http_host); buffer_to_lower(con->uri.authority); - + + config_patch_connection(srv, con, COMP_HTTP_SCHEME); /* Scheme: */ config_patch_connection(srv, con, COMP_HTTP_HOST); /* Host: */ - config_patch_connection(srv, con, COMP_HTTP_REMOTEIP); /* Client-IP */ + config_patch_connection(srv, con, COMP_HTTP_REMOTE_IP); /* Client-IP */ config_patch_connection(srv, con, COMP_HTTP_REFERER); /* Referer: */ - config_patch_connection(srv, con, COMP_HTTP_USERAGENT); /* User-Agent: */ + config_patch_connection(srv, con, COMP_HTTP_USER_AGENT);/* User-Agent: */ + config_patch_connection(srv, con, COMP_HTTP_LANGUAGE); /* Accept-Language: */ config_patch_connection(srv, con, COMP_HTTP_COOKIE); /* Cookie: */ - + config_patch_connection(srv, con, COMP_HTTP_REQUEST_METHOD); /* REQUEST_METHOD */ + + /** their might be a fragment which has to be cut away */ + if (NULL != (qstr = strchr(con->request.uri->ptr, '#'))) { + con->request.uri->used = qstr - con->request.uri->ptr; + con->request.uri->ptr[con->request.uri->used++] = '\0'; + } + /** extract query string from request.uri */ if (NULL != (qstr = strchr(con->request.uri->ptr, '?'))) { buffer_copy_string (con->uri.query, qstr + 1); @@ -200,22 +304,16 @@ handler_t http_response_prepare(server *srv, connection *con) { log_error_write(srv, __FILE__, __LINE__, "sb", "URI-path : ", con->uri.path_raw); log_error_write(srv, __FILE__, __LINE__, "sb", "URI-query : ", con->uri.query); } - - /* disable keep-alive if requested */ - - if (con->request_count > con->conf.max_keep_alive_requests) { - con->keep_alive = 0; - } - - + + /** - * - * call plugins - * + * + * call plugins + * * - based on the raw URL - * + * */ - + switch(r = plugins_call_handle_uri_raw(srv, con)) { case HANDLER_GO_ON: break; @@ -229,14 +327,14 @@ handler_t http_response_prepare(server *srv, connection *con) { break; } - /* build filename + /* build filename * * - decode url-encodings (e.g. %20 -> ' ') * - remove path-modifiers (e.g. /../) */ - - - + + + if (con->request.http_method == HTTP_METHOD_OPTIONS && con->uri.path_raw->ptr[0] == '*' && con->uri.path_raw->ptr[1] == '\0') { /* OPTIONS * ... */ @@ -252,16 +350,28 @@ handler_t http_response_prepare(server *srv, connection *con) { log_error_write(srv, __FILE__, __LINE__, "sb", "URI-path : ", con->uri.path); } +#ifdef USE_OPENSSL + if (con->conf.is_ssl && con->conf.ssl_verifyclient) { + https_add_ssl_entries(con); + } +#endif + /** - * - * call plugins - * + * + * call plugins + * * - based on the clean URL - * + * */ - + config_patch_connection(srv, con, COMP_HTTP_URL); /* HTTPurl */ - + config_patch_connection(srv, con, COMP_HTTP_QUERY_STRING); /* HTTPqs */ + + /* do we have to downgrade to 1.0 ? */ + if (!con->conf.allow_http11) { + con->request.http_version = HTTP_VERSION_1_0; + } + switch(r = plugins_call_handle_uri_clean(srv, con)) { case HANDLER_GO_ON: break; @@ -274,11 +384,11 @@ handler_t http_response_prepare(server *srv, connection *con) { log_error_write(srv, __FILE__, __LINE__, ""); break; } - + if (con->request.http_method == HTTP_METHOD_OPTIONS && con->uri.path->ptr[0] == '*' && con->uri.path_raw->ptr[1] == '\0') { /* option requests are handled directly without checking of the path */ - + response_header_insert(srv, con, CONST_STR_LEN("Allow"), CONST_STR_LEN("OPTIONS, GET, HEAD, POST")); con->http_status = 200; @@ -288,38 +398,74 @@ handler_t http_response_prepare(server *srv, connection *con) { } /*** - * - * border - * + * + * border + * * logical filename (URI) becomes a physical filename here - * - * - * + * + * + * */ - - - - + + + + /* 1. stat() * ... ISREG() -> ok, go on * ... ISDIR() -> index-file -> redirect - * - * 2. pathinfo() + * + * 2. pathinfo() * ... ISREG() - * + * * 3. -> 404 - * + * */ - + /* * SEARCH DOCUMENT ROOT */ - + /* set a default */ - + buffer_copy_string_buffer(con->physical.doc_root, con->conf.document_root); buffer_copy_string_buffer(con->physical.rel_path, con->uri.path); - + +#if defined(__WIN32) || defined(__CYGWIN__) + /* strip dots from the end and spaces + * + * windows/dos handle those filenames as the same file + * + * foo == foo. == foo..... == "foo... " == "foo.. ./" + * + * This will affect in some cases PATHINFO + * + * on native windows we could prepend the filename with \\?\ to circumvent + * this behaviour. I have no idea how to push this through cygwin + * + * */ + + if (con->physical.rel_path->used > 1) { + buffer *b = con->physical.rel_path; + size_t i; + + if (b->used > 2 && + b->ptr[b->used-2] == '/' && + (b->ptr[b->used-3] == ' ' || + b->ptr[b->used-3] == '.')) { + b->ptr[b->used--] = '\0'; + } + + for (i = b->used - 2; b->used > 1; i--) { + if (b->ptr[i] == ' ' || + b->ptr[i] == '.') { + b->ptr[b->used--] = '\0'; + } else { + break; + } + } + } +#endif + if (con->conf.log_request_handling) { log_error_write(srv, __FILE__, __LINE__, "s", "-- before doc_root"); log_error_write(srv, __FILE__, __LINE__, "sb", "Doc-Root :", con->physical.doc_root); @@ -341,9 +487,9 @@ handler_t http_response_prepare(server *srv, connection *con) { log_error_write(srv, __FILE__, __LINE__, ""); break; } - - /* MacOS X and Windows can't distiguish between upper and lower-case - * + + /* MacOS X and Windows can't distiguish between upper and lower-case + * * convert to lower-case */ if (con->conf.force_lowercase_filenames) { @@ -354,13 +500,13 @@ handler_t http_response_prepare(server *srv, connection *con) { if (buffer_is_empty(con->server_name)) { buffer_copy_string_buffer(con->server_name, con->uri.authority); } - - /** - * create physical filename + + /** + * create physical filename * -> physical.path = docroot + rel_path - * + * */ - + buffer_copy_string_buffer(con->physical.path, con->physical.doc_root); BUFFER_APPEND_SLASH(con->physical.path); buffer_copy_string_buffer(con->physical.basedir, con->physical.path); @@ -390,7 +536,7 @@ handler_t http_response_prepare(server *srv, connection *con) { log_error_write(srv, __FILE__, __LINE__, ""); break; } - + if (con->conf.log_request_handling) { log_error_write(srv, __FILE__, __LINE__, "s", "-- logical -> physical"); log_error_write(srv, __FILE__, __LINE__, "sb", "Doc-Root :", con->physical.doc_root); @@ -398,41 +544,57 @@ handler_t http_response_prepare(server *srv, connection *con) { log_error_write(srv, __FILE__, __LINE__, "sb", "Path :", con->physical.path); } } - - /* + + /* * Noone catched away the file from normal path of execution yet (like mod_access) - * + * * Go on and check of the file exists at all */ - + if (con->mode == DIRECT) { char *slash = NULL; char *pathinfo = NULL; int found = 0; stat_cache_entry *sce = NULL; - + if (con->conf.log_request_handling) { log_error_write(srv, __FILE__, __LINE__, "s", "-- handling physical path"); log_error_write(srv, __FILE__, __LINE__, "sb", "Path :", con->physical.path); } - + if (HANDLER_ERROR != stat_cache_get_entry(srv, con, con->physical.path, &sce)) { /* file exists */ - + if (con->conf.log_request_handling) { log_error_write(srv, __FILE__, __LINE__, "s", "-- file found"); log_error_write(srv, __FILE__, __LINE__, "sb", "Path :", con->physical.path); } - +#ifdef HAVE_LSTAT + if ((sce->is_symlink != 0) && !con->conf.follow_symlink) { + con->http_status = 403; + + if (con->conf.log_request_handling) { + log_error_write(srv, __FILE__, __LINE__, "s", "-- access denied due symlink restriction"); + log_error_write(srv, __FILE__, __LINE__, "sb", "Path :", con->physical.path); + } + + buffer_reset(con->physical.path); + return HANDLER_FINISHED; + }; +#endif if (S_ISDIR(sce->st.st_mode)) { - if (con->physical.path->ptr[con->physical.path->used - 2] != '/') { + if (con->uri.path->ptr[con->uri.path->used - 2] != '/') { /* redirect to .../ */ - + http_response_redirect_to_directory(srv, con); - + return HANDLER_FINISHED; } +#ifdef HAVE_LSTAT + } else if (!S_ISREG(sce->st.st_mode) && !sce->is_symlink) { +#else } else if (!S_ISREG(sce->st.st_mode)) { +#endif /* any special handling of non-reg files ?*/ @@ -441,14 +603,16 @@ handler_t http_response_prepare(server *srv, connection *con) { switch (errno) { case EACCES: con->http_status = 403; - + if (con->conf.log_request_handling) { log_error_write(srv, __FILE__, __LINE__, "s", "-- access denied"); log_error_write(srv, __FILE__, __LINE__, "sb", "Path :", con->physical.path); } - + buffer_reset(con->physical.path); return HANDLER_FINISHED; + case ENAMETOOLONG: + /* file name to be read was too long. return 404 */ case ENOENT: con->http_status = 404; @@ -466,74 +630,85 @@ handler_t http_response_prepare(server *srv, connection *con) { /* we have no idea what happend. let's tell the user so. */ con->http_status = 500; buffer_reset(con->physical.path); - + log_error_write(srv, __FILE__, __LINE__, "ssbsb", "file not found ... or so: ", strerror(errno), con->uri.path, "->", con->physical.path); - + return HANDLER_FINISHED; } - + /* not found, perhaps PATHINFO */ - + buffer_copy_string_buffer(srv->tmp_buf, con->physical.path); - + do { - struct stat st; - if (slash) { buffer_copy_string_len(con->physical.path, srv->tmp_buf->ptr, slash - srv->tmp_buf->ptr); } else { buffer_copy_string_buffer(con->physical.path, srv->tmp_buf); } - - if (0 == stat(con->physical.path->ptr, &(st)) && - S_ISREG(st.st_mode)) { - found = 1; + + if (HANDLER_ERROR != stat_cache_get_entry(srv, con, con->physical.path, &sce)) { + found = S_ISREG(sce->st.st_mode); break; } - + if (pathinfo != NULL) { *pathinfo = '\0'; } slash = strrchr(srv->tmp_buf->ptr, '/'); - + if (pathinfo != NULL) { /* restore '/' */ *pathinfo = '/'; } - + if (slash) pathinfo = slash; - } while ((found == 0) && (slash != NULL) && (slash - srv->tmp_buf->ptr > con->physical.basedir->used - 2)); - + } while ((found == 0) && (slash != NULL) && ((size_t)(slash - srv->tmp_buf->ptr) > (con->physical.basedir->used - 2))); + if (found == 0) { /* no it really doesn't exists */ con->http_status = 404; - + if (con->conf.log_file_not_found) { log_error_write(srv, __FILE__, __LINE__, "sbsb", "file not found:", con->uri.path, "->", con->physical.path); } - + buffer_reset(con->physical.path); - + return HANDLER_FINISHED; } - + +#ifdef HAVE_LSTAT + if ((sce->is_symlink != 0) && !con->conf.follow_symlink) { + con->http_status = 403; + + if (con->conf.log_request_handling) { + log_error_write(srv, __FILE__, __LINE__, "s", "-- access denied due symlink restriction"); + log_error_write(srv, __FILE__, __LINE__, "sb", "Path :", con->physical.path); + } + + buffer_reset(con->physical.path); + return HANDLER_FINISHED; + }; +#endif + /* we have a PATHINFO */ if (pathinfo) { buffer_copy_string(con->request.pathinfo, pathinfo); - + /* * shorten uri.path */ - + con->uri.path->used -= strlen(pathinfo); con->uri.path->ptr[con->uri.path->used - 1] = '\0'; } - + if (con->conf.log_request_handling) { log_error_write(srv, __FILE__, __LINE__, "s", "-- after pathinfo check"); log_error_write(srv, __FILE__, __LINE__, "sb", "Path :", con->physical.path); @@ -541,12 +716,12 @@ handler_t http_response_prepare(server *srv, connection *con) { log_error_write(srv, __FILE__, __LINE__, "sb", "Pathinfo :", con->request.pathinfo); } } - + if (con->conf.log_request_handling) { log_error_write(srv, __FILE__, __LINE__, "s", "-- handling subrequest"); log_error_write(srv, __FILE__, __LINE__, "sb", "Path :", con->physical.path); } - + /* call the handlers */ switch(r = plugins_call_handle_subrequest_start(srv, con)) { case HANDLER_GO_ON: @@ -557,21 +732,27 @@ handler_t http_response_prepare(server *srv, connection *con) { if (con->conf.log_request_handling) { log_error_write(srv, __FILE__, __LINE__, "s", "-- subrequest finished"); } - + /* something strange happend */ return r; } - + /* if we are still here, no one wanted the file, status 403 is ok I think */ - - if (con->mode == DIRECT) { - con->http_status = 403; - + + if (con->mode == DIRECT && con->http_status == 0) { + switch (con->request.http_method) { + case HTTP_METHOD_OPTIONS: + con->http_status = 200; + break; + default: + con->http_status = 403; + } + return HANDLER_FINISHED; } - + } - + switch(r = plugins_call_handle_subrequest(srv, con)) { case HANDLER_GO_ON: /* request was not handled, looks like we are done */ @@ -582,7 +763,7 @@ handler_t http_response_prepare(server *srv, connection *con) { /* something strange happend */ return r; } - + /* can't happen */ return HANDLER_COMEBACK; } diff --git a/src/response.h b/src/response.h index c9ff234..289add8 100644 --- a/src/response.h +++ b/src/response.h @@ -1,15 +1,16 @@ #ifndef _RESPONSE_H_ #define _RESPONSE_H_ -#include <time.h> - #include "server.h" +#include <time.h> + int http_response_parse(server *srv, connection *con); int http_response_write_header(server *srv, connection *con); int response_header_insert(server *srv, connection *con, const char *key, size_t keylen, const char *value, size_t vallen); int response_header_overwrite(server *srv, connection *con, const char *key, size_t keylen, const char *value, size_t vallen); +int response_header_append(server *srv, connection *con, const char *key, size_t keylen, const char *value, size_t vallen); handler_t http_response_prepare(server *srv, connection *con); int http_response_redirect_to_directory(server *srv, connection *con); diff --git a/src/server.c b/src/server.c index 9eb9eb4..590a9d5 100644 --- a/src/server.c +++ b/src/server.c @@ -1,3 +1,20 @@ +#include "server.h" +#include "buffer.h" +#include "network.h" +#include "log.h" +#include "keyvalue.h" +#include "response.h" +#include "request.h" +#include "chunk.h" +#include "http_chunk.h" +#include "fdevent.h" +#include "connections.h" +#include "stat_cache.h" +#include "plugin.h" +#include "joblist.h" +#include "network_backends.h" +#include "version.h" + #include <sys/types.h> #include <sys/time.h> #include <sys/stat.h> @@ -14,44 +31,33 @@ #include <stdio.h> -#include "server.h" -#include "buffer.h" -#include "network.h" -#include "log.h" -#include "keyvalue.h" -#include "response.h" -#include "request.h" -#include "chunk.h" -#include "http_chunk.h" -#include "fdevent.h" -#include "connections.h" -#include "stat_cache.h" -#include "plugin.h" -#include "joblist.h" - #ifdef HAVE_GETOPT_H -#include <getopt.h> +# include <getopt.h> #endif #ifdef HAVE_VALGRIND_VALGRIND_H -#include <valgrind/valgrind.h> +# include <valgrind/valgrind.h> #endif #ifdef HAVE_SYS_WAIT_H -#include <sys/wait.h> +# include <sys/wait.h> #endif #ifdef HAVE_PWD_H -#include <grp.h> -#include <pwd.h> +# include <grp.h> +# include <pwd.h> #endif #ifdef HAVE_SYS_RESOURCE_H -#include <sys/resource.h> +# include <sys/resource.h> #endif #ifdef HAVE_SYS_PRCTL_H -#include <sys/prctl.h> +# include <sys/prctl.h> +#endif + +#ifdef USE_OPENSSL +# include <openssl/err.h> #endif #ifndef __sgi @@ -59,35 +65,76 @@ /* #define USE_ALARM */ #endif +#ifdef HAVE_GETUID +# ifndef HAVE_ISSETUGID + +static int l_issetugid(void) { + return (geteuid() != getuid() || getegid() != getgid()); +} + +# define issetugid l_issetugid +# endif +#endif + static volatile sig_atomic_t srv_shutdown = 0; static volatile sig_atomic_t graceful_shutdown = 0; static volatile sig_atomic_t handle_sig_alarm = 1; static volatile sig_atomic_t handle_sig_hup = 0; +static volatile sig_atomic_t forwarded_sig_hup = 0; #if defined(HAVE_SIGACTION) && defined(SA_SIGINFO) +static volatile siginfo_t last_sigterm_info; +static volatile siginfo_t last_sighup_info; + static void sigaction_handler(int sig, siginfo_t *si, void *context) { - UNUSED(si); + static siginfo_t empty_siginfo; UNUSED(context); - switch (sig) { - case SIGTERM: srv_shutdown = 1; break; - case SIGINT: - if (graceful_shutdown) srv_shutdown = 1; - else graceful_shutdown = 1; + if (!si) si = &empty_siginfo; - break; - case SIGALRM: handle_sig_alarm = 1; break; - case SIGHUP: handle_sig_hup = 1; break; - case SIGCHLD: break; + switch (sig) { + case SIGTERM: + srv_shutdown = 1; + last_sigterm_info = *si; + break; + case SIGINT: + if (graceful_shutdown) { + srv_shutdown = 1; + } else { + graceful_shutdown = 1; + } + last_sigterm_info = *si; + + break; + case SIGALRM: + handle_sig_alarm = 1; + break; + case SIGHUP: + /** + * we send the SIGHUP to all procs in the process-group + * this includes ourself + * + * make sure we only send it once and don't create a + * infinite loop + */ + if (!forwarded_sig_hup) { + handle_sig_hup = 1; + last_sighup_info = *si; + } else { + forwarded_sig_hup = 0; + } + break; + case SIGCHLD: + break; } } #elif defined(HAVE_SIGNAL) || defined(HAVE_SIGACTION) static void signal_handler(int sig) { switch (sig) { case SIGTERM: srv_shutdown = 1; break; - case SIGINT: + case SIGINT: if (graceful_shutdown) srv_shutdown = 1; - else graceful_shutdown = 1; + else graceful_shutdown = 1; break; case SIGALRM: handle_sig_alarm = 1; break; @@ -109,27 +156,26 @@ static void daemonize(void) { signal(SIGTSTP, SIG_IGN); #endif if (0 != fork()) exit(0); - + if (-1 == setsid()) exit(0); signal(SIGHUP, SIG_IGN); if (0 != fork()) exit(0); - + if (0 != chdir("/")) exit(0); - - umask(0); } #endif static server *server_init(void) { int i; - + FILE *frandom = NULL; + server *srv = calloc(1, sizeof(*srv)); assert(srv); #define CLEAN(x) \ srv->x = buffer_init(); - + CLEAN(response_header); CLEAN(parse_full_path); CLEAN(ts_debug_str); @@ -139,66 +185,84 @@ static server *server_init(void) { CLEAN(tmp_buf); srv->empty_string = buffer_init_string(""); CLEAN(cond_check_buf); - + CLEAN(srvconf.errorlog_file); + CLEAN(srvconf.breakagelog_file); CLEAN(srvconf.groupname); CLEAN(srvconf.username); CLEAN(srvconf.changeroot); CLEAN(srvconf.bindhost); CLEAN(srvconf.event_handler); CLEAN(srvconf.pid_file); - + CLEAN(tmp_chunk_len); #undef CLEAN - + #define CLEAN(x) \ srv->x = array_init(); - + CLEAN(config_context); CLEAN(config_touched); CLEAN(status); #undef CLEAN - + for (i = 0; i < FILE_CACHE_MAX; i++) { + srv->mtime_cache[i].mtime = (time_t)-1; srv->mtime_cache[i].str = buffer_init(); } - + + if ((NULL != (frandom = fopen("/dev/urandom", "rb")) || NULL != (frandom = fopen("/dev/random", "rb"))) + && 1 == fread(srv->entropy, sizeof(srv->entropy), 1, frandom)) { + unsigned int e; + memcpy(&e, srv->entropy, sizeof(e) < sizeof(srv->entropy) ? sizeof(e) : sizeof(srv->entropy)); + srand(e); + srv->is_real_entropy = 1; + } else { + unsigned int j; + srand(time(NULL) ^ getpid()); + srv->is_real_entropy = 0; + for (j = 0; j < sizeof(srv->entropy); j++) + srv->entropy[j] = rand(); + } + if (frandom) fclose(frandom); + srv->cur_ts = time(NULL); srv->startup_ts = srv->cur_ts; - + srv->conns = calloc(1, sizeof(*srv->conns)); assert(srv->conns); - + srv->joblist = calloc(1, sizeof(*srv->joblist)); assert(srv->joblist); - + srv->fdwaitqueue = calloc(1, sizeof(*srv->fdwaitqueue)); assert(srv->fdwaitqueue); - + srv->srvconf.modules = array_init(); srv->srvconf.modules_dir = buffer_init_string(LIBRARY_DIR); srv->srvconf.network_backend = buffer_init(); srv->srvconf.upload_tempdirs = array_init(); - + srv->srvconf.reject_expect_100_with_417 = 1; + /* use syslog */ - srv->errorlog_fd = -1; - srv->errorlog_mode = ERRORLOG_STDERR; + srv->errorlog_fd = STDERR_FILENO; + srv->errorlog_mode = ERRORLOG_FD; srv->split_vals = array_init(); - + return srv; } static void server_free(server *srv) { size_t i; - + for (i = 0; i < FILE_CACHE_MAX; i++) { buffer_free(srv->mtime_cache[i].str); } - + #define CLEAN(x) \ buffer_free(srv->x); - + CLEAN(response_header); CLEAN(parse_full_path); CLEAN(ts_debug_str); @@ -208,8 +272,9 @@ static void server_free(server *srv) { CLEAN(tmp_buf); CLEAN(empty_string); CLEAN(cond_check_buf); - + CLEAN(srvconf.errorlog_file); + CLEAN(srvconf.breakagelog_file); CLEAN(srvconf.groupname); CLEAN(srvconf.username); CLEAN(srvconf.changeroot); @@ -217,7 +282,8 @@ static void server_free(server *srv) { CLEAN(srvconf.event_handler); CLEAN(srvconf.pid_file); CLEAN(srvconf.modules_dir); - + CLEAN(srvconf.network_backend); + CLEAN(tmp_chunk_len); #undef CLEAN @@ -225,48 +291,64 @@ static void server_free(server *srv) { fdevent_unregister(srv->ev, srv->fd); #endif fdevent_free(srv->ev); - + free(srv->conns); - + if (srv->config_storage) { for (i = 0; i < srv->config_context->used; i++) { specific_config *s = srv->config_storage[i]; if (!s) continue; - + buffer_free(s->document_root); buffer_free(s->server_name); buffer_free(s->server_tag); buffer_free(s->ssl_pemfile); buffer_free(s->ssl_ca_file); + buffer_free(s->ssl_cipher_list); + buffer_free(s->ssl_dh_file); + buffer_free(s->ssl_ec_curve); buffer_free(s->error_handler); buffer_free(s->errorfile_prefix); array_free(s->mimetypes); - + buffer_free(s->ssl_verifyclient_username); +#ifdef USE_OPENSSL + SSL_CTX_free(s->ssl_ctx); +#endif free(s); } free(srv->config_storage); srv->config_storage = NULL; } - + #define CLEAN(x) \ array_free(srv->x); - + CLEAN(config_context); CLEAN(config_touched); CLEAN(status); + CLEAN(srvconf.upload_tempdirs); #undef CLEAN - + joblist_free(srv, srv->joblist); fdwaitqueue_free(srv, srv->fdwaitqueue); - + if (srv->stat_cache) { stat_cache_free(srv->stat_cache); } array_free(srv->srvconf.modules); array_free(srv->split_vals); - + +#ifdef USE_OPENSSL + if (srv->ssl_is_init) { + CRYPTO_cleanup_all_ex_data(); + ERR_free_strings(); + ERR_remove_state(0); + EVP_cleanup(); + } +#endif + free(srv); } @@ -276,21 +358,166 @@ static void show_version (void) { #else # define TEXT_SSL #endif - char *b = PACKAGE_NAME "-" PACKAGE_VERSION TEXT_SSL \ + char *b = PACKAGE_DESC TEXT_SSL \ " - a light and fast webserver\n" \ "Build-Date: " __DATE__ " " __TIME__ "\n"; ; -#undef TEXT_SSL +#undef TEXT_SSL write(STDOUT_FILENO, b, strlen(b)); } +static void show_features (void) { + const char features[] = "" +#ifdef USE_SELECT + "\t+ select (generic)\n" +#else + "\t- select (generic)\n" +#endif +#ifdef USE_POLL + "\t+ poll (Unix)\n" +#else + "\t- poll (Unix)\n" +#endif +#ifdef USE_LINUX_SIGIO + "\t+ rt-signals (Linux 2.4+)\n" +#else + "\t- rt-signals (Linux 2.4+)\n" +#endif +#ifdef USE_LINUX_EPOLL + "\t+ epoll (Linux 2.6)\n" +#else + "\t- epoll (Linux 2.6)\n" +#endif +#ifdef USE_SOLARIS_DEVPOLL + "\t+ /dev/poll (Solaris)\n" +#else + "\t- /dev/poll (Solaris)\n" +#endif +#ifdef USE_SOLARIS_PORT + "\t+ eventports (Solaris)\n" +#else + "\t- eventports (Solaris)\n" +#endif +#ifdef USE_FREEBSD_KQUEUE + "\t+ kqueue (FreeBSD)\n" +#else + "\t- kqueue (FreeBSD)\n" +#endif +#ifdef USE_LIBEV + "\t+ libev (generic)\n" +#else + "\t- libev (generic)\n" +#endif + "\nNetwork handler:\n\n" +#if defined USE_LINUX_SENDFILE + "\t+ linux-sendfile\n" +#else + "\t- linux-sendfile\n" +#endif +#if defined USE_FREEBSD_SENDFILE + "\t+ freebsd-sendfile\n" +#else + "\t- freebsd-sendfile\n" +#endif +#if defined USE_SOLARIS_SENDFILEV + "\t+ solaris-sendfilev\n" +#else + "\t- solaris-sendfilev\n" +#endif +#if defined USE_WRITEV + "\t+ writev\n" +#else + "\t- writev\n" +#endif + "\t+ write\n" +#ifdef USE_MMAP + "\t+ mmap support\n" +#else + "\t- mmap support\n" +#endif + "\nFeatures:\n\n" +#ifdef HAVE_IPV6 + "\t+ IPv6 support\n" +#else + "\t- IPv6 support\n" +#endif +#if defined HAVE_ZLIB_H && defined HAVE_LIBZ + "\t+ zlib support\n" +#else + "\t- zlib support\n" +#endif +#if defined HAVE_BZLIB_H && defined HAVE_LIBBZ2 + "\t+ bzip2 support\n" +#else + "\t- bzip2 support\n" +#endif +#ifdef HAVE_LIBCRYPT + "\t+ crypt support\n" +#else + "\t- crypt support\n" +#endif +#ifdef USE_OPENSSL + "\t+ SSL Support\n" +#else + "\t- SSL Support\n" +#endif +#ifdef HAVE_LIBPCRE + "\t+ PCRE support\n" +#else + "\t- PCRE support\n" +#endif +#ifdef HAVE_MYSQL + "\t+ mySQL support\n" +#else + "\t- mySQL support\n" +#endif +#if defined(HAVE_LDAP_H) && defined(HAVE_LBER_H) && defined(HAVE_LIBLDAP) && defined(HAVE_LIBLBER) + "\t+ LDAP support\n" +#else + "\t- LDAP support\n" +#endif +#ifdef HAVE_MEMCACHE_H + "\t+ memcached support\n" +#else + "\t- memcached support\n" +#endif +#ifdef HAVE_FAM_H + "\t+ FAM support\n" +#else + "\t- FAM support\n" +#endif +#ifdef HAVE_LUA_H + "\t+ LUA support\n" +#else + "\t- LUA support\n" +#endif +#ifdef HAVE_LIBXML_H + "\t+ xml support\n" +#else + "\t- xml support\n" +#endif +#ifdef HAVE_SQLITE3_H + "\t+ SQLite support\n" +#else + "\t- SQLite support\n" +#endif +#ifdef HAVE_GDBM_H + "\t+ GDBM support\n" +#else + "\t- GDBM support\n" +#endif + "\n"; + show_version(); + printf("\nEvent Handlers:\n\n%s", features); +} + static void show_help (void) { #ifdef USE_OPENSSL # define TEXT_SSL " (ssl)" #else # define TEXT_SSL #endif - char *b = PACKAGE_NAME "-" PACKAGE_VERSION TEXT_SSL " ("__DATE__ " " __TIME__ ")" \ + char *b = PACKAGE_DESC TEXT_SSL " ("__DATE__ " " __TIME__ ")" \ " - a light and fast webserver\n" \ "usage:\n" \ " -f <name> filename of the config-file\n" \ @@ -299,10 +526,11 @@ static void show_help (void) { " -t test the config-file, and exit\n" \ " -D don't go to background (default: go to background)\n" \ " -v show version\n" \ +" -V show compile-time features\n" \ " -h show this help\n" \ "\n" ; -#undef TEXT_SSL +#undef TEXT_SSL #undef TEXT_IPV6 write(STDOUT_FILENO, b, strlen(b)); } @@ -322,27 +550,27 @@ int main (int argc, char **argv) { #ifdef HAVE_GETRLIMIT struct rlimit rlim; #endif - + #ifdef USE_ALARM struct itimerval interval; - + interval.it_interval.tv_sec = 1; interval.it_interval.tv_usec = 0; interval.it_value.tv_sec = 1; interval.it_value.tv_usec = 0; #endif - - + + /* for nice %b handling in strfime() */ setlocale(LC_TIME, "C"); - + if (NULL == (srv = server_init())) { fprintf(stderr, "did this really happen?\n"); return -1; } - + /* init structs done */ - + srv->srvconf.port = 0; #ifdef HAVE_GETUID i_am_root = (getuid() == 0); @@ -350,11 +578,18 @@ int main (int argc, char **argv) { i_am_root = 0; #endif srv->srvconf.dont_daemonize = 0; - - while(-1 != (o = getopt(argc, argv, "f:m:hvDpt"))) { + + while(-1 != (o = getopt(argc, argv, "f:m:hvVDpt"))) { switch(o) { - case 'f': - if (config_read(srv, optarg)) { + case 'f': + if (srv->config_storage) { + log_error_write(srv, __FILE__, __LINE__, "s", + "Can only read one config file. Use the include command to use multiple config files."); + + server_free(srv); + return -1; + } + if (config_read(srv, optarg)) { server_free(srv); return -1; } @@ -366,27 +601,28 @@ int main (int argc, char **argv) { case 't': test_config = 1; break; case 'D': srv->srvconf.dont_daemonize = 1; break; case 'v': show_version(); return 0; + case 'V': show_features(); return 0; case 'h': show_help(); return 0; - default: + default: show_help(); server_free(srv); return -1; } } - + if (!srv->config_storage) { log_error_write(srv, __FILE__, __LINE__, "s", "No configuration available. Try using -f option."); - + server_free(srv); return -1; } - + if (print_config) { data_unset *dc = srv->config_context->data[0]; if (dc) { dc->print(dc, 0); - fprintf(stderr, "\n"); + fprintf(stdout, "\n"); } else { /* shouldn't happend */ fprintf(stderr, "global config not found\n"); @@ -401,62 +637,51 @@ int main (int argc, char **argv) { server_free(srv); return 0; } - + /* close stdin and stdout, as they are not needed */ - /* move stdin to /dev/null */ - if (-1 != (fd = open("/dev/null", O_RDONLY))) { - close(STDIN_FILENO); - dup2(fd, STDIN_FILENO); - close(fd); - } - - /* move stdout to /dev/null */ - if (-1 != (fd = open("/dev/null", O_WRONLY))) { - close(STDOUT_FILENO); - dup2(fd, STDOUT_FILENO); - close(fd); - } - + openDevNull(STDIN_FILENO); + openDevNull(STDOUT_FILENO); + if (0 != config_set_defaults(srv)) { - log_error_write(srv, __FILE__, __LINE__, "s", + log_error_write(srv, __FILE__, __LINE__, "s", "setting default values failed"); server_free(srv); return -1; } - + /* UID handling */ #ifdef HAVE_GETUID - if (!i_am_root && (geteuid() == 0 || getegid() == 0)) { + if (!i_am_root && issetugid()) { /* we are setuid-root */ - - log_error_write(srv, __FILE__, __LINE__, "s", + + log_error_write(srv, __FILE__, __LINE__, "s", "Are you nuts ? Don't apply a SUID bit to this binary"); - + server_free(srv); return -1; } #endif - + /* check document-root */ if (srv->config_storage[0]->document_root->used <= 1) { - log_error_write(srv, __FILE__, __LINE__, "s", + log_error_write(srv, __FILE__, __LINE__, "s", "document-root is not set\n"); - + server_free(srv); - + return -1; } - + if (plugins_load(srv)) { log_error_write(srv, __FILE__, __LINE__, "s", "loading plugins finally failed"); - + plugins_free(srv); server_free(srv); - + return -1; } - + /* open pid file BEFORE chroot */ if (srv->srvconf.pid_file->used) { if (-1 == (pid_fd = open(srv->srvconf.pid_file->ptr, O_WRONLY | O_CREAT | O_EXCL | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH))) { @@ -466,18 +691,18 @@ int main (int argc, char **argv) { "opening pid-file failed:", srv->srvconf.pid_file, strerror(errno)); return -1; } - + if (0 != stat(srv->srvconf.pid_file->ptr, &st)) { log_error_write(srv, __FILE__, __LINE__, "sbs", "stating existing pid-file failed:", srv->srvconf.pid_file, strerror(errno)); } - + if (!S_ISREG(st.st_mode)) { log_error_write(srv, __FILE__, __LINE__, "sb", "pid-file exists and isn't regular file:", srv->srvconf.pid_file); return -1; } - + if (-1 == (pid_fd = open(srv->srvconf.pid_file->ptr, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH))) { log_error_write(srv, __FILE__, __LINE__, "sbs", "opening pid-file failed:", srv->srvconf.pid_file, strerror(errno)); @@ -504,7 +729,7 @@ int main (int argc, char **argv) { #ifdef HAVE_VALGRIND_VALGRIND_H if (RUNNING_ON_VALGRIND) use_rlimit = 0; #endif - + #ifdef HAVE_GETRLIMIT if (0 != getrlimit(RLIMIT_NOFILE, &rlim)) { log_error_write(srv, __FILE__, __LINE__, @@ -512,13 +737,13 @@ int main (int argc, char **argv) { strerror(errno)); return -1; } - + if (use_rlimit && srv->srvconf.max_fds) { /* set rlimits */ - + rlim.rlim_cur = srv->srvconf.max_fds; rlim.rlim_max = srv->srvconf.max_fds; - + if (0 != setrlimit(RLIMIT_NOFILE, &rlim)) { log_error_write(srv, __FILE__, __LINE__, "ss", "couldn't set 'max filedescriptors'", @@ -527,11 +752,8 @@ int main (int argc, char **argv) { } } - /* #372: solaris need some fds extra for devpoll */ - if (rlim.rlim_cur > 10) rlim.rlim_cur -= 10; - if (srv->event_handler == FDEVENT_HANDLER_SELECT) { - srv->max_fds = rlim.rlim_cur < FD_SETSIZE - 200 ? rlim.rlim_cur : FD_SETSIZE - 200; + srv->max_fds = rlim.rlim_cur < ((int)FD_SETSIZE) - 200 ? rlim.rlim_cur : FD_SETSIZE - 200; } else { srv->max_fds = rlim.rlim_cur; } @@ -544,34 +766,34 @@ int main (int argc, char **argv) { #endif if (srv->event_handler == FDEVENT_HANDLER_SELECT) { /* don't raise the limit above FD_SET_SIZE */ - if (srv->max_fds > FD_SETSIZE - 200) { - log_error_write(srv, __FILE__, __LINE__, "sd", + if (srv->max_fds > ((int)FD_SETSIZE) - 200) { + log_error_write(srv, __FILE__, __LINE__, "sd", "can't raise max filedescriptors above", FD_SETSIZE - 200, "if event-handler is 'select'. Use 'poll' or something else or reduce server.max-fds."); return -1; } } - + #ifdef HAVE_PWD_H /* set user and group */ if (srv->srvconf.username->used) { if (NULL == (pwd = getpwnam(srv->srvconf.username->ptr))) { - log_error_write(srv, __FILE__, __LINE__, "sb", + log_error_write(srv, __FILE__, __LINE__, "sb", "can't find username", srv->srvconf.username); return -1; } - + if (pwd->pw_uid == 0) { log_error_write(srv, __FILE__, __LINE__, "s", "I will not set uid to 0\n"); return -1; } } - + if (srv->srvconf.groupname->used) { if (NULL == (grp = getgrnam(srv->srvconf.groupname->ptr))) { - log_error_write(srv, __FILE__, __LINE__, "sb", + log_error_write(srv, __FILE__, __LINE__, "sb", "can't find groupname", srv->srvconf.groupname); return -1; } @@ -581,15 +803,28 @@ int main (int argc, char **argv) { return -1; } } -#endif +#endif /* we need root-perms for port < 1024 */ if (0 != network_init(srv)) { plugins_free(srv); server_free(srv); - + return -1; } -#ifdef HAVE_CHROOT +#ifdef HAVE_PWD_H + /* + * Change group before chroot, when we have access + * to /etc/group + * */ + if (NULL != grp) { + setgid(grp->gr_gid); + setgroups(0, NULL); + if (srv->srvconf.username->used) { + initgroups(srv->srvconf.username->ptr, grp->gr_gid); + } + } +#endif +#ifdef HAVE_CHROOT if (srv->srvconf.changeroot->used) { tzset(); @@ -605,15 +840,14 @@ int main (int argc, char **argv) { #endif #ifdef HAVE_PWD_H /* drop root privs */ - if (srv->srvconf.groupname->used) { - setgid(grp->gr_gid); - setgroups(0, NULL); + if (NULL != pwd) { + setuid(pwd->pw_uid); } - if (srv->srvconf.username->used && srv->srvconf.groupname->used) - initgroups(srv->srvconf.username->ptr, grp->gr_gid); - if (srv->srvconf.username->used) setuid(pwd->pw_uid); #endif -#ifdef HAVE_PRCTL +#if defined(HAVE_SYS_PRCTL_H) && defined(PR_SET_DUMPABLE) + /** + * on IRIX 6.5.30 they have prctl() but no DUMPABLE + */ if (srv->srvconf.enable_cores) { prctl(PR_SET_DUMPABLE, 1, 0, 0, 0); } @@ -628,8 +862,24 @@ int main (int argc, char **argv) { return -1; } + /** + * we are not root can can't increase the fd-limit, but we can reduce it + */ + if (srv->srvconf.max_fds && srv->srvconf.max_fds < rlim.rlim_cur) { + /* set rlimits */ + + rlim.rlim_cur = srv->srvconf.max_fds; + + if (0 != setrlimit(RLIMIT_NOFILE, &rlim)) { + log_error_write(srv, __FILE__, __LINE__, + "ss", "couldn't set 'max filedescriptors'", + strerror(errno)); + return -1; + } + } + if (srv->event_handler == FDEVENT_HANDLER_SELECT) { - srv->max_fds = rlim.rlim_cur < FD_SETSIZE - 200 ? rlim.rlim_cur : FD_SETSIZE - 200; + srv->max_fds = rlim.rlim_cur < ((int)FD_SETSIZE) - 200 ? rlim.rlim_cur : FD_SETSIZE - 200; } else { srv->max_fds = rlim.rlim_cur; } @@ -643,71 +893,83 @@ int main (int argc, char **argv) { #endif if (srv->event_handler == FDEVENT_HANDLER_SELECT) { /* don't raise the limit above FD_SET_SIZE */ - if (srv->max_fds > FD_SETSIZE - 200) { - log_error_write(srv, __FILE__, __LINE__, "sd", + if (srv->max_fds > ((int)FD_SETSIZE) - 200) { + log_error_write(srv, __FILE__, __LINE__, "sd", "can't raise max filedescriptors above", FD_SETSIZE - 200, "if event-handler is 'select'. Use 'poll' or something else or reduce server.max-fds."); return -1; } } - + if (0 != network_init(srv)) { plugins_free(srv); server_free(srv); - + return -1; } } /* set max-conns */ - if (srv->srvconf.max_conns > srv->max_fds) { - /* we can't have more connections than max-fds */ - srv->max_conns = srv->max_fds; + if (srv->srvconf.max_conns > srv->max_fds/2) { + /* we can't have more connections than max-fds/2 */ + log_error_write(srv, __FILE__, __LINE__, "sdd", "can't have more connections than fds/2: ", srv->srvconf.max_conns, srv->max_fds); + srv->max_conns = srv->max_fds/2; } else if (srv->srvconf.max_conns) { /* otherwise respect the wishes of the user */ srv->max_conns = srv->srvconf.max_conns; } else { - /* or use the default */ - srv->max_conns = srv->max_fds; + /* or use the default: we really don't want to hit max-fds */ + srv->max_conns = srv->max_fds/3; } - + if (HANDLER_GO_ON != plugins_call_init(srv)) { log_error_write(srv, __FILE__, __LINE__, "s", "Initialization of plugins failed. Going down."); - + plugins_free(srv); network_close(srv); server_free(srv); - + return -1; } -#ifdef HAVE_FORK +#ifdef HAVE_FORK /* network is up, let's deamonize ourself */ if (srv->srvconf.dont_daemonize == 0) daemonize(); #endif srv->gid = getgid(); srv->uid = getuid(); - + /* write pid file */ if (pid_fd != -1) { buffer_copy_long(srv->tmp_buf, getpid()); - buffer_append_string(srv->tmp_buf, "\n"); + buffer_append_string_len(srv->tmp_buf, CONST_STR_LEN("\n")); write(pid_fd, srv->tmp_buf->ptr, srv->tmp_buf->used - 1); close(pid_fd); pid_fd = -1; } - + + /* Close stderr ASAP in the child process to make sure that nothing + * is being written to that fd which may not be valid anymore. */ + if (-1 == log_error_open(srv)) { + log_error_write(srv, __FILE__, __LINE__, "s", "Opening errorlog failed. Going down."); + + plugins_free(srv); + network_close(srv); + server_free(srv); + return -1; + } + if (HANDLER_GO_ON != plugins_call_set_defaults(srv)) { log_error_write(srv, __FILE__, __LINE__, "s", "Configuration of plugins failed. Going down."); - + plugins_free(srv); network_close(srv); server_free(srv); - + return -1; } - + /* dump unused config-keys */ for (i = 0; i < srv->config_context->used; i++) { array *config = ((data_config *)srv->config_context->data[i])->value; @@ -715,43 +977,42 @@ int main (int argc, char **argv) { for (j = 0; config && j < config->used; j++) { data_unset *du = config->data[j]; - + /* all var.* is known as user defined variable */ if (strncmp(du->key->ptr, "var.", sizeof("var.") - 1) == 0) { continue; } if (NULL == array_get_element(srv->config_touched, du->key->ptr)) { - log_error_write(srv, __FILE__, __LINE__, "sbs", + log_error_write(srv, __FILE__, __LINE__, "sbs", "WARNING: unknown config-key:", du->key, "(ignored)"); } } } - + + if (srv->config_unsupported) { + log_error_write(srv, __FILE__, __LINE__, "s", + "Configuration contains unsupported keys. Going down."); + } + if (srv->config_deprecated) { - log_error_write(srv, __FILE__, __LINE__, "s", + log_error_write(srv, __FILE__, __LINE__, "s", "Configuration contains deprecated keys. Going down."); - - plugins_free(srv); - network_close(srv); - server_free(srv); - - return -1; } - - if (-1 == log_error_open(srv)) { - log_error_write(srv, __FILE__, __LINE__, "s", - "opening errorlog failed, dying"); - + + if (srv->config_unsupported || srv->config_deprecated) { plugins_free(srv); network_close(srv); server_free(srv); + return -1; } - - + + + + #ifdef HAVE_SIGACTION memset(&act, 0, sizeof(act)); act.sa_handler = SIG_IGN; @@ -771,7 +1032,7 @@ int main (int argc, char **argv) { sigaction(SIGHUP, &act, NULL); sigaction(SIGALRM, &act, NULL); sigaction(SIGCHLD, &act, NULL); - + #elif defined(HAVE_SIGNAL) /* ignore the SIGPIPE from sendfile() */ signal(SIGPIPE, SIG_IGN); @@ -782,25 +1043,25 @@ int main (int argc, char **argv) { signal(SIGCHLD, signal_handler); signal(SIGINT, signal_handler); #endif - + #ifdef USE_ALARM signal(SIGALRM, signal_handler); - + /* setup periodic timer (1 second) */ if (setitimer(ITIMER_REAL, &interval, NULL)) { log_error_write(srv, __FILE__, __LINE__, "s", "setting timer failed"); return -1; } - + getitimer(ITIMER_REAL, &interval); #endif -#ifdef HAVE_FORK +#ifdef HAVE_FORK /* start watcher and workers */ num_childs = srv->srvconf.max_worker; if (num_childs > 0) { int child = 0; - while (!child && !srv_shutdown) { + while (!child && !srv_shutdown && !graceful_shutdown) { if (num_childs > 0) { switch (fork()) { case -1: @@ -814,24 +1075,80 @@ int main (int argc, char **argv) { } } else { int status; - wait(&status); - num_childs++; + + if (-1 != wait(&status)) { + /** + * one of our workers went away + */ + num_childs++; + } else { + switch (errno) { + case EINTR: + /** + * if we receive a SIGHUP we have to close our logs ourself as we don't + * have the mainloop who can help us here + */ + if (handle_sig_hup) { + handle_sig_hup = 0; + + log_error_cycle(srv); + + /** + * forward to all procs in the process-group + * + * we also send it ourself + */ + if (!forwarded_sig_hup) { + forwarded_sig_hup = 1; + kill(0, SIGHUP); + } + } + break; + default: + break; + } + } } } - if (srv_shutdown) { - kill(0, SIGTERM); + + /** + * for the parent this is the exit-point + */ + if (!child) { + /** + * kill all children too + */ + if (graceful_shutdown) { + kill(0, SIGINT); + } else if (srv_shutdown) { + kill(0, SIGTERM); + } + + log_error_close(srv); + network_close(srv); + connections_free(srv); + plugins_free(srv); + server_free(srv); + return 0; } - if (!child) return 0; } #endif - if (NULL == (srv->ev = fdevent_init(srv->max_fds + 1, srv->event_handler))) { + if (NULL == (srv->ev = fdevent_init(srv, srv->max_fds + 1, srv->event_handler))) { log_error_write(srv, __FILE__, __LINE__, "s", "fdevent_init failed"); return -1; } - /* - * kqueue() is called here, select resets its internals, + + /* libev backend overwrites our SIGCHLD handler and calls waitpid on SIGCHLD; we want our own SIGCHLD handling. */ +#ifdef HAVE_SIGACTION + sigaction(SIGCHLD, &act, NULL); +#elif defined(HAVE_SIGNAL) + signal(SIGCHLD, signal_handler); +#endif + + /* + * kqueue() is called here, select resets its internals, * all server sockets get their handlers * * */ @@ -839,7 +1156,7 @@ int main (int argc, char **argv) { plugins_free(srv); network_close(srv); server_free(srv); - + return -1; } @@ -854,6 +1171,8 @@ int main (int argc, char **argv) { /* setup FAM */ if (srv->srvconf.stat_cache_engine == STAT_CACHE_ENGINE_FAM) { if (0 != FAMOpen2(srv->stat_cache->fam, "lighttpd")) { + log_error_write(srv, __FILE__, __LINE__, "s", + "could not open a fam connection, dieing."); return -1; } #ifdef HAVE_FAMNOEXISTS @@ -862,7 +1181,7 @@ int main (int argc, char **argv) { srv->stat_cache->fam_fcce_ndx = -1; fdevent_register(srv->ev, FAMCONNECTION_GETFD(srv->stat_cache->fam), stat_cache_handle_fdevent, NULL); - fdevent_event_add(srv->ev, &(srv->stat_cache->fam_fcce_ndx), FAMCONNECTION_GETFD(srv->stat_cache->fam), FDEVENT_IN); + fdevent_event_set(srv->ev, &(srv->stat_cache->fam_fcce_ndx), FAMCONNECTION_GETFD(srv->stat_cache->fam), FDEVENT_IN); } #endif @@ -884,16 +1203,16 @@ int main (int argc, char **argv) { int n; size_t ndx; time_t min_ts; - + if (handle_sig_hup) { handler_t r; - + /* reset notification */ handle_sig_hup = 0; - - + + /* cycle logfiles */ - + switch(r = plugins_call_handle_sighup(srv)) { case HANDLER_GO_ON: break; @@ -901,30 +1220,41 @@ int main (int argc, char **argv) { log_error_write(srv, __FILE__, __LINE__, "sd", "sighup-handler return with an error", r); break; } - + if (-1 == log_error_cycle(srv)) { log_error_write(srv, __FILE__, __LINE__, "s", "cycling errorlog failed, dying"); - + return -1; + } else { +#ifdef HAVE_SIGACTION + log_error_write(srv, __FILE__, __LINE__, "sdsd", + "logfiles cycled UID =", + last_sighup_info.si_uid, + "PID =", + last_sighup_info.si_pid); +#else + log_error_write(srv, __FILE__, __LINE__, "s", + "logfiles cycled"); +#endif } } - + if (handle_sig_alarm) { /* a new second */ - + #ifdef USE_ALARM /* reset notification */ handle_sig_alarm = 0; #endif - + /* get current time */ min_ts = time(NULL); - + if (min_ts != srv->cur_ts) { int cs = 0; connections *conns = srv->conns; handler_t r; - + switch(r = plugins_call_handle_trigger(srv)) { case HANDLER_GO_ON: break; @@ -935,21 +1265,21 @@ int main (int argc, char **argv) { log_error_write(srv, __FILE__, __LINE__, "d", r); break; } - + /* trigger waitpid */ srv->cur_ts = min_ts; - - /* cleanup stat-cache */ + + /* cleanup stat-cache */ stat_cache_trigger_cleanup(srv); /** - * check all connections for timeouts - * + * check all connections for timeouts + * */ for (ndx = 0; ndx < conns->used; ndx++) { int changed = 0; connection *con; int t_diff; - + con = conns->ptr[ndx]; if (con->state == CON_STATE_READ || @@ -958,17 +1288,17 @@ int main (int argc, char **argv) { if (srv->cur_ts - con->read_idle_ts > con->conf.max_read_idle) { /* time - out */ #if 0 - log_error_write(srv, __FILE__, __LINE__, "sd", + log_error_write(srv, __FILE__, __LINE__, "sd", "connection closed - read-timeout:", con->fd); #endif connection_set_state(srv, con, CON_STATE_ERROR); changed = 1; } } else { - if (srv->cur_ts - con->read_idle_ts > con->conf.max_keep_alive_idle) { + if (srv->cur_ts - con->read_idle_ts > con->keep_alive_idle) { /* time - out */ #if 0 - log_error_write(srv, __FILE__, __LINE__, "sd", + log_error_write(srv, __FILE__, __LINE__, "sd", "connection closed - read-timeout:", con->fd); #endif connection_set_state(srv, con, CON_STATE_ERROR); @@ -976,20 +1306,20 @@ int main (int argc, char **argv) { } } } - + if ((con->state == CON_STATE_WRITE) && - (con->write_request_ts != 0)) { + (con->write_request_ts != 0)) { #if 0 if (srv->cur_ts - con->write_request_ts > 60) { - log_error_write(srv, __FILE__, __LINE__, "sdd", + log_error_write(srv, __FILE__, __LINE__, "sdd", "connection closed - pre-write-request-timeout:", con->fd, srv->cur_ts - con->write_request_ts); } #endif - + if (srv->cur_ts - con->write_request_ts > con->conf.max_write_idle) { /* time - out */ -#if 1 - log_error_write(srv, __FILE__, __LINE__, "sbsosds", + if (con->conf.log_timeouts) { + log_error_write(srv, __FILE__, __LINE__, "sbsosds", "NOTE: a request for", con->request.uri, "timed out after writing", @@ -997,42 +1327,47 @@ int main (int argc, char **argv) { "bytes. We waited", (int)con->conf.max_write_idle, "seconds. If this a problem increase server.max-write-idle"); -#endif + } connection_set_state(srv, con, CON_STATE_ERROR); changed = 1; } } + + if (con->state == CON_STATE_CLOSE && (srv->cur_ts - con->close_timeout_ts > HTTP_LINGER_TIMEOUT)) { + changed = 1; + } + /* we don't like div by zero */ if (0 == (t_diff = srv->cur_ts - con->connection_start)) t_diff = 1; - - if (con->traffic_limit_reached && - (con->conf.kbytes_per_second == 0 || + + if (con->traffic_limit_reached && + (con->conf.kbytes_per_second == 0 || ((con->bytes_written / t_diff) < con->conf.kbytes_per_second * 1024))) { /* enable connection again */ con->traffic_limit_reached = 0; - + changed = 1; } - + if (changed) { connection_state_machine(srv, con); } con->bytes_written_cur_second = 0; *(con->conf.global_bytes_per_second_cnt_ptr) = 0; - + #if 0 if (cs == 0) { fprintf(stderr, "connection-state: "); cs = 1; } - + fprintf(stderr, "c[%d,%d]: %s ", con->fd, con->fcgi.fd, connection_get_state(con->state)); #endif } - + if (cs == 1) fprintf(stderr, "\n"); } } @@ -1040,25 +1375,25 @@ int main (int argc, char **argv) { if (srv->sockets_disabled) { /* our server sockets are disabled, why ? */ - if ((srv->cur_fds + srv->want_fds < srv->max_fds * 0.8) && /* we have enough unused fds */ - (srv->conns->used < srv->max_conns * 0.9) && + if ((srv->cur_fds + srv->want_fds < srv->max_fds * 8 / 10) && /* we have enough unused fds */ + (srv->conns->used <= srv->max_conns * 9 / 10) && (0 == graceful_shutdown)) { for (i = 0; i < srv->srv_sockets.used; i++) { server_socket *srv_socket = srv->srv_sockets.ptr[i]; - fdevent_event_add(srv->ev, &(srv_socket->fde_ndx), srv_socket->fd, FDEVENT_IN); + fdevent_event_set(srv->ev, &(srv_socket->fde_ndx), srv_socket->fd, FDEVENT_IN); } - + log_error_write(srv, __FILE__, __LINE__, "s", "[note] sockets enabled again"); - + srv->sockets_disabled = 0; } } else { - if ((srv->cur_fds + srv->want_fds > srv->max_fds * 0.9) || /* out of fds */ - (srv->conns->used > srv->max_conns) || /* out of connections */ - (graceful_shutdown)) { /* graceful_shutdown */ + if ((srv->cur_fds + srv->want_fds > srv->max_fds * 9 / 10) || /* out of fds */ + (srv->conns->used >= srv->max_conns) || /* out of connections */ + (graceful_shutdown)) { /* graceful_shutdown */ /* disable server-fds */ - + for (i = 0; i < srv->srv_sockets.used; i++) { server_socket *srv_socket = srv->srv_sockets.ptr[i]; fdevent_event_del(srv->ev, &(srv_socket->fde_ndx), srv_socket->fd); @@ -1075,17 +1410,30 @@ int main (int argc, char **argv) { srv_socket->fd = -1; /* network_close() will cleanup after us */ + + if (srv->srvconf.pid_file->used && + srv->srvconf.changeroot->used == 0) { + if (0 != unlink(srv->srvconf.pid_file->ptr)) { + if (errno != EACCES && errno != EPERM) { + log_error_write(srv, __FILE__, __LINE__, "sbds", + "unlink failed for:", + srv->srvconf.pid_file, + errno, + strerror(errno)); + } + } + } } } - + if (graceful_shutdown) { log_error_write(srv, __FILE__, __LINE__, "s", "[note] graceful shutdown started"); - } else if (srv->conns->used > srv->max_conns) { + } else if (srv->conns->used >= srv->max_conns) { log_error_write(srv, __FILE__, __LINE__, "s", "[note] sockets disabled, connection limit reached"); } else { log_error_write(srv, __FILE__, __LINE__, "s", "[note] sockets disabled, out-of-fds"); } - + srv->sockets_disabled = 1; } } @@ -1095,16 +1443,16 @@ int main (int argc, char **argv) { * we are ready to terminate without harming anyone */ srv_shutdown = 1; } - + /* we still have some fds to share */ - if (srv->want_fds) { + if (srv->want_fds) { /* check the fdwaitqueue for waiting fds */ int free_fds = srv->max_fds - srv->cur_fds - 16; connection *con; - + for (; free_fds > 0 && NULL != (con = fdwaitqueue_unshift(srv, srv->fdwaitqueue)); free_fds--) { connection_state_machine(srv, con); - + srv->want_fds--; } } @@ -1115,27 +1463,29 @@ int main (int argc, char **argv) { int fd_ndx; #if 0 if (n > 0) { - log_error_write(srv, __FILE__, __LINE__, "sd", + log_error_write(srv, __FILE__, __LINE__, "sd", "polls:", n); } -#endif +#endif fd_ndx = -1; do { fdevent_handler handler; void *context; handler_t r; - + fd_ndx = fdevent_event_next_fdndx (srv->ev, fd_ndx); + if (-1 == fd_ndx) break; /* not all fdevent handlers know how many fds got an event */ + revents = fdevent_event_get_revent (srv->ev, fd_ndx); fd = fdevent_event_get_fd (srv->ev, fd_ndx); handler = fdevent_get_handler(srv->ev, fd); context = fdevent_get_context(srv->ev, fd); - + /* connection_handle_fdevent needs a joblist_append */ #if 0 - log_error_write(srv, __FILE__, __LINE__, "sdd", + log_error_write(srv, __FILE__, __LINE__, "sdd", "event for", fd, revents); -#endif +#endif switch (r = (*handler)(srv, context, revents)) { case HANDLER_FINISHED: case HANDLER_GO_ON: @@ -1152,17 +1502,17 @@ int main (int argc, char **argv) { } } while (--n > 0); } else if (n < 0 && errno != EINTR) { - log_error_write(srv, __FILE__, __LINE__, "ss", - "fdevent_poll failed:", + log_error_write(srv, __FILE__, __LINE__, "ss", + "fdevent_poll failed:", strerror(errno)); } - + for (ndx = 0; ndx < srv->joblist->used; ndx++) { connection *con = srv->joblist->ptr[ndx]; handler_t r; - + connection_state_machine(srv, con); - + switch(r = plugins_call_handle_joblist(srv, con)) { case HANDLER_FINISHED: case HANDLER_GO_ON: @@ -1171,32 +1521,44 @@ int main (int argc, char **argv) { log_error_write(srv, __FILE__, __LINE__, "d", r); break; } - + con->in_joblist = 0; } - + srv->joblist->used = 0; } - + if (srv->srvconf.pid_file->used && - srv->srvconf.changeroot->used == 0) { + srv->srvconf.changeroot->used == 0 && + 0 == graceful_shutdown) { if (0 != unlink(srv->srvconf.pid_file->ptr)) { if (errno != EACCES && errno != EPERM) { - log_error_write(srv, __FILE__, __LINE__, "sbds", - "unlink failed for:", + log_error_write(srv, __FILE__, __LINE__, "sbds", + "unlink failed for:", srv->srvconf.pid_file, errno, strerror(errno)); } } } - + +#ifdef HAVE_SIGACTION + log_error_write(srv, __FILE__, __LINE__, "sdsd", + "server stopped by UID =", + last_sigterm_info.si_uid, + "PID =", + last_sigterm_info.si_pid); +#else + log_error_write(srv, __FILE__, __LINE__, "s", + "server stopped"); +#endif + /* clean-up */ log_error_close(srv); network_close(srv); connections_free(srv); plugins_free(srv); server_free(srv); - + return 0; } diff --git a/src/settings.h b/src/settings.h index f0c6354..137a0a8 100644 --- a/src/settings.h +++ b/src/settings.h @@ -1,6 +1,14 @@ #ifndef _LIGHTTPD_SETTINGS_H_ #define _LIGHTTPD_SETTINGS_H_ +#ifndef _GNU_SOURCE +# define _GNU_SOURCE +#endif + +#ifndef __USE_GNU +# define __USE_GNU /* a hack in my eyes, <fcntl.h> F_SETSIG should work with _GNU_SOURCE */ +#endif + #define BV(x) (1 << x) #define INET_NTOP_CACHE_MAX 4 @@ -9,28 +17,33 @@ /** * max size of a buffer which will just be reset * to ->used = 0 instead of really freeing the buffer - * + * * 64kB (no real reason, just a guess) */ #define BUFFER_MAX_REUSE_SIZE (4 * 1024) +/* both should be way smaller than SSIZE_MAX :) */ +#define MAX_READ_LIMIT (256*1024) +#define MAX_WRITE_LIMIT (256*1024) + /** * max size of the HTTP request header - * + * * 32k should be enough for everything (just a guess) - * + * */ #define MAX_HTTP_REQUEST_HEADER (32 * 1024) -typedef enum { HANDLER_UNSET, - HANDLER_GO_ON, +typedef enum { HANDLER_UNSET, + HANDLER_GO_ON, HANDLER_FINISHED, - HANDLER_COMEBACK, - HANDLER_WAIT_FOR_EVENT, + HANDLER_COMEBACK, + HANDLER_WAIT_FOR_EVENT, HANDLER_ERROR, HANDLER_WAIT_FOR_FD } handler_t; +#define HTTP_LINGER_TIMEOUT 5 /* we use it in a enum */ #ifdef TRUE diff --git a/src/spawn-fcgi.c b/src/spawn-fcgi.c deleted file mode 100644 index 99fc31a..0000000 --- a/src/spawn-fcgi.c +++ /dev/null @@ -1,431 +0,0 @@ -#include <sys/types.h> -#include <sys/time.h> -#include <sys/stat.h> - -#include <stdlib.h> -#include <string.h> -#include <errno.h> -#include <stdio.h> -#include <unistd.h> -#include <fcntl.h> - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - - -#ifdef HAVE_PWD_H -#include <grp.h> -#include <pwd.h> -#endif - -#ifdef HAVE_GETOPT_H -#include <getopt.h> -#endif - -#define FCGI_LISTENSOCK_FILENO 0 - -#ifndef UNIX_PATH_MAX -# define UNIX_PATH_MAX 108 -#endif - -#include "sys-socket.h" - -#ifdef HAVE_SYS_WAIT_H -#include <sys/wait.h> -#endif - -/* for solaris 2.5 and netbsd 1.3.x */ -#ifndef HAVE_SOCKLEN_T -typedef int socklen_t; -#endif - -#ifdef HAVE_SYS_UN_H -int fcgi_spawn_connection(char *appPath, unsigned short port, const char *unixsocket, int child_count, int pid_fd, int nofork) { - int fcgi_fd; - int socket_type, status; - struct timeval tv = { 0, 100 * 1000 }; - - struct sockaddr_un fcgi_addr_un; - struct sockaddr_in fcgi_addr_in; - struct sockaddr *fcgi_addr; - - socklen_t servlen; - - if (child_count < 2) { - child_count = 5; - } - - if (child_count > 256) { - child_count = 256; - } - - - if (unixsocket) { - memset(&fcgi_addr, 0, sizeof(fcgi_addr)); - - fcgi_addr_un.sun_family = AF_UNIX; - strcpy(fcgi_addr_un.sun_path, unixsocket); - -#ifdef SUN_LEN - servlen = SUN_LEN(&fcgi_addr_un); -#else - /* stevens says: */ - servlen = strlen(fcgi_addr_un.sun_path) + sizeof(fcgi_addr_un.sun_family); -#endif - socket_type = AF_UNIX; - fcgi_addr = (struct sockaddr *) &fcgi_addr_un; - } else { - fcgi_addr_in.sin_family = AF_INET; - fcgi_addr_in.sin_addr.s_addr = htonl(INADDR_ANY); - fcgi_addr_in.sin_port = htons(port); - servlen = sizeof(fcgi_addr_in); - - socket_type = AF_INET; - fcgi_addr = (struct sockaddr *) &fcgi_addr_in; - } - - if (-1 == (fcgi_fd = socket(socket_type, SOCK_STREAM, 0))) { - fprintf(stderr, "%s.%d\n", - __FILE__, __LINE__); - return -1; - } - - if (-1 == connect(fcgi_fd, fcgi_addr, servlen)) { - /* server is not up, spawn in */ - pid_t child; - int val; - - if (unixsocket) unlink(unixsocket); - - close(fcgi_fd); - - /* reopen socket */ - if (-1 == (fcgi_fd = socket(socket_type, SOCK_STREAM, 0))) { - fprintf(stderr, "%s.%d\n", - __FILE__, __LINE__); - return -1; - } - - val = 1; - if (setsockopt(fcgi_fd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val)) < 0) { - fprintf(stderr, "%s.%d\n", - __FILE__, __LINE__); - return -1; - } - - /* create socket */ - if (-1 == bind(fcgi_fd, fcgi_addr, servlen)) { - fprintf(stderr, "%s.%d: bind failed: %s\n", - __FILE__, __LINE__, - strerror(errno)); - return -1; - } - - if (-1 == listen(fcgi_fd, 1024)) { - fprintf(stderr, "%s.%d: fd = -1\n", - __FILE__, __LINE__); - return -1; - } - - if (!nofork) { - child = fork(); - } else { - child = 0; - } - - switch (child) { - case 0: { - char cgi_childs[64]; - char *b; - - int i = 0; - - /* is save as we limit to 256 childs */ - sprintf(cgi_childs, "PHP_FCGI_CHILDREN=%d", child_count); - - if(fcgi_fd != FCGI_LISTENSOCK_FILENO) { - close(FCGI_LISTENSOCK_FILENO); - dup2(fcgi_fd, FCGI_LISTENSOCK_FILENO); - close(fcgi_fd); - } - - /* we don't need the client socket */ - for (i = 3; i < 256; i++) { - close(i); - } - - /* create environment */ - - putenv(cgi_childs); - - /* fork and replace shell */ - b = malloc(strlen("exec ") + strlen(appPath) + 1); - strcpy(b, "exec "); - strcat(b, appPath); - - /* exec the cgi */ - execl("/bin/sh", "sh", "-c", b, NULL); - - exit(errno); - - break; - } - case -1: - /* error */ - break; - default: - /* father */ - - /* wait */ - select(0, NULL, NULL, NULL, &tv); - - switch (waitpid(child, &status, WNOHANG)) { - case 0: - fprintf(stderr, "%s.%d: child spawned successfully: PID: %d\n", - __FILE__, __LINE__, - child); - - /* write pid file */ - if (pid_fd != -1) { - /* assume a 32bit pid_t */ - char pidbuf[12]; - - snprintf(pidbuf, sizeof(pidbuf) - 1, "%d", child); - - write(pid_fd, pidbuf, strlen(pidbuf)); - close(pid_fd); - pid_fd = -1; - } - - break; - case -1: - break; - default: - if (WIFEXITED(status)) { - fprintf(stderr, "%s.%d: child exited with: %d, %s\n", - __FILE__, __LINE__, - WEXITSTATUS(status), strerror(WEXITSTATUS(status))); - } else if (WIFSIGNALED(status)) { - fprintf(stderr, "%s.%d: child signaled: %d\n", - __FILE__, __LINE__, - WTERMSIG(status)); - } else { - fprintf(stderr, "%s.%d: child died somehow: %d\n", - __FILE__, __LINE__, - status); - } - } - - break; - } - } else { - fprintf(stderr, "%s.%d: socket is already used, can't spawn\n", - __FILE__, __LINE__); - return -1; - } - - close(fcgi_fd); - - return 0; -} - - -void show_version () { - char *b = "spawn-fcgi" "-" PACKAGE_VERSION \ -" - spawns fastcgi processes\n" -; - write(1, b, strlen(b)); -} - -void show_help () { - char *b = "spawn-fcgi" "-" PACKAGE_VERSION \ -" - spawns fastcgi processes\n" \ -"usage:\n" \ -" -f <fcgiapp> filename of the fcgi-application\n" \ -" -p <port> bind to tcp-port\n" \ -" -s <path> bind to unix-domain socket\n" \ -" -C <childs> (PHP only) numbers of childs to spawn (default 5)\n" \ -" -P <path> name of PID-file for spawed process\n" \ -" -n no fork (for daemontools)\n" \ -" -v show version\n" \ -" -h show this help\n" \ -"(root only)\n" \ -" -c <dir> chroot to directory\n" \ -" -u <user> change to user-id\n" \ -" -g <group> change to group-id\n" \ -; - write(1, b, strlen(b)); -} - - -int main(int argc, char **argv) { - char *fcgi_app = NULL, *changeroot = NULL, *username = NULL, - *groupname = NULL, *unixsocket = NULL, *pid_file = NULL; - unsigned short port = 0; - int child_count = 5; - int i_am_root, o; - int pid_fd = -1; - int nofork = 0; - - i_am_root = (getuid() == 0); - - while(-1 != (o = getopt(argc, argv, "c:f:g:hnp:u:vC:s:P:"))) { - switch(o) { - case 'f': fcgi_app = optarg; break; - case 'p': port = strtol(optarg, NULL, 10);/* port */ break; - case 'C': child_count = strtol(optarg, NULL, 10);/* */ break; - case 's': unixsocket = optarg; /* unix-domain socket */ break; - case 'c': if (i_am_root) { changeroot = optarg; }/* chroot() */ break; - case 'u': if (i_am_root) { username = optarg; } /* set user */ break; - case 'g': if (i_am_root) { groupname = optarg; } /* set group */ break; - case 'n': nofork = 1; break; - case 'P': pid_file = optarg; /* PID file */ break; - case 'v': show_version(); return 0; - case 'h': show_help(); return 0; - default: - show_help(); - return -1; - } - } - - if (fcgi_app == NULL || (port == 0 && unixsocket == NULL)) { - show_help(); - return -1; - } - - if (unixsocket && port) { - fprintf(stderr, "%s.%d: %s\n", - __FILE__, __LINE__, - "either a unix domain socket or a tcp-port, but not both\n"); - - return -1; - } - - if (unixsocket && strlen(unixsocket) > UNIX_PATH_MAX - 1) { - fprintf(stderr, "%s.%d: %s\n", - __FILE__, __LINE__, - "path of the unix socket is too long\n"); - - return -1; - } - - /* UID handling */ - if (!i_am_root && (geteuid() == 0 || getegid() == 0)) { - /* we are setuid-root */ - - fprintf(stderr, "%s.%d: %s\n", - __FILE__, __LINE__, - "Are you nuts ? Don't apply a SUID bit to this binary\n"); - - return -1; - } - - if (pid_file && - (-1 == (pid_fd = open(pid_file, O_WRONLY | O_CREAT | O_EXCL | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)))) { - struct stat st; - if (errno != EEXIST) { - fprintf(stderr, "%s.%d: opening pid-file '%s' failed: %s\n", - __FILE__, __LINE__, - pid_file, strerror(errno)); - - return -1; - } - - /* ok, file exists */ - - if (0 != stat(pid_file, &st)) { - fprintf(stderr, "%s.%d: stating pid-file '%s' failed: %s\n", - __FILE__, __LINE__, - pid_file, strerror(errno)); - - return -1; - } - - /* is it a regular file ? */ - - if (!S_ISREG(st.st_mode)) { - fprintf(stderr, "%s.%d: pid-file exists and isn't regular file: '%s'\n", - __FILE__, __LINE__, - pid_file); - - return -1; - } - - if (-1 == (pid_fd = open(pid_file, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH))) { - fprintf(stderr, "%s.%d: opening pid-file '%s' failed: %s\n", - __FILE__, __LINE__, - pid_file, strerror(errno)); - - return -1; - } - } - - if (i_am_root) { - struct group *grp = NULL; - struct passwd *pwd = NULL; - - /* set user and group */ - - if (username) { - if (NULL == (pwd = getpwnam(username))) { - fprintf(stderr, "%s.%d: %s, %s\n", - __FILE__, __LINE__, - "can't find username", username); - return -1; - } - - if (pwd->pw_uid == 0) { - fprintf(stderr, "%s.%d: %s\n", - __FILE__, __LINE__, - "I will not set uid to 0\n"); - return -1; - } - } - - if (groupname) { - if (NULL == (grp = getgrnam(groupname))) { - fprintf(stderr, "%s.%d: %s %s\n", - __FILE__, __LINE__, - "can't find groupname", - groupname); - return -1; - } - if (grp->gr_gid == 0) { - fprintf(stderr, "%s.%d: %s\n", - __FILE__, __LINE__, - "I will not set gid to 0\n"); - return -1; - } - } - - if (changeroot) { - if (-1 == chroot(changeroot)) { - fprintf(stderr, "%s.%d: %s %s\n", - __FILE__, __LINE__, - "chroot failed: ", strerror(errno)); - return -1; - } - if (-1 == chdir("/")) { - fprintf(stderr, "%s.%d: %s %s\n", - __FILE__, __LINE__, - "chdir failed: ", strerror(errno)); - return -1; - } - } - - /* drop root privs */ - if (groupname) { - setgid(grp->gr_gid); - setgroups(0, NULL); - } - if (username) setuid(pwd->pw_uid); - } - - return fcgi_spawn_connection(fcgi_app, port, unixsocket, child_count, pid_fd, nofork); -} -#else -int main() { - return -1; -} -#endif diff --git a/src/splaytree.c b/src/splaytree.c index 3a80910..51aa0ca 100644 --- a/src/splaytree.c +++ b/src/splaytree.c @@ -46,9 +46,9 @@ Addison-Wesley, 1993, pp 367-375 */ +#include "splaytree.h" #include <stdlib.h> #include <assert.h> -#include "splaytree.h" #define compare(i,j) ((i)-(j)) /* This is the comparison. */ @@ -56,19 +56,18 @@ #define node_size splaytree_size -/* Splay using the key i (which may or may not be in the tree.) - * The starting root is t, and the tree used is defined by rat +/* Splay using the key i (which may or may not be in the tree.) + * The starting root is t, and the tree used is defined by rat * size fields are maintained */ splay_tree * splaytree_splay (splay_tree *t, int i) { splay_tree N, *l, *r, *y; - int comp, root_size, l_size, r_size; - + int comp, l_size, r_size; + if (t == NULL) return t; N.left = N.right = NULL; l = r = &N; - root_size = node_size(t); l_size = r_size = 0; - + for (;;) { comp = compare(i, t->key); if (comp < 0) { @@ -120,7 +119,7 @@ splay_tree * splaytree_splay (splay_tree *t, int i) { y->size = r_size; r_size -= 1+node_size(y->right); } - + l->right = t->left; /* assemble */ r->left = t->right; t->left = N.right; @@ -187,7 +186,8 @@ splay_tree * splaytree_delete(splay_tree *t, int i) { } } -splay_tree *find_rank(int r, splay_tree *t) { +#if 0 +static splay_tree *find_rank(int r, splay_tree *t) { /* Returns a pointer to the node in the tree with the given rank. */ /* Returns NULL if there is no such node. */ /* Does not change the tree. To guarantee logarithmic behavior, */ @@ -206,5 +206,4 @@ splay_tree *find_rank(int r, splay_tree *t) { } } } - - +#endif diff --git a/src/splaytree.h b/src/splaytree.h index 98e4234..4be1523 100644 --- a/src/splaytree.h +++ b/src/splaytree.h @@ -19,6 +19,6 @@ splay_tree * splaytree_size(splay_tree *t); /* This macro returns the size of a node. Unlike "x->size", */ /* it works even if x=NULL. The test could be avoided by using */ /* a special version of NULL which was a real node with size 0. */ - + #endif diff --git a/src/stat_cache.c b/src/stat_cache.c index 148f4c8..3edaeeb 100644 --- a/src/stat_cache.c +++ b/src/stat_cache.c @@ -1,4 +1,7 @@ -#define _GNU_SOURCE +#include "log.h" +#include "stat_cache.h" +#include "fdevent.h" +#include "etag.h" #include <sys/types.h> #include <sys/stat.h> @@ -11,13 +14,8 @@ #include <fcntl.h> #include <assert.h> -#include "log.h" -#include "stat_cache.h" -#include "fdevent.h" -#include "etag.h" - #ifdef HAVE_ATTR_ATTRIBUTES_H -#include <attr/attributes.h> +# include <attr/attributes.h> #endif #ifdef HAVE_FAM_H @@ -36,7 +34,7 @@ #endif #ifndef HAVE_LSTAT -#define lstat stat +# define lstat stat #endif #if 0 @@ -52,8 +50,8 @@ * * if we get a change-event from FAM, we increment the version in the FAM->dir mapping * - * if the stat()-cache is queried we check if the version id for the directory is the - * same and return immediatly. + * if the stat()-cache is queried we check if the version id for the directory is the + * same and return immediatly. * * * What we need: @@ -62,17 +60,17 @@ * - for each FAMRequest we have to find the version in the directory cache (index as userdata) * * stat <<-> directory <-> FAMRequest - * - * if file is deleted, directory is dirty, file is rechecked ... + * + * if file is deleted, directory is dirty, file is rechecked ... * if directory is deleted, directory mapping is removed - * + * * */ #ifdef HAVE_FAM_H typedef struct { FAMRequest *req; FAMConnection *fc; - + buffer *name; int version; @@ -83,16 +81,16 @@ typedef struct { * - we need a hash * - the hash-key is used as sorting criteria for a tree * - a splay-tree is used as we can use the caching effect of it - */ + */ /* we want to cleanup the stat-cache every few seconds, let's say 10 * * - remove entries which are outdated since 30s * - remove entries which are fresh but havn't been used since 60s * - if we don't have a stat-cache entry for a directory, release it from the monitor - */ + */ -#ifdef DEBUG_STAT_CACHE +#ifdef DEBUG_STAT_CACHE typedef struct { int *ptr; @@ -105,15 +103,16 @@ static fake_keys ctrl; stat_cache *stat_cache_init(void) { stat_cache *fc = NULL; - + fc = calloc(1, sizeof(*fc)); - + fc->dir_name = buffer_init(); + fc->hash_key = buffer_init(); #ifdef HAVE_FAM_H fc->fam = calloc(1, sizeof(*fc->fam)); #endif -#ifdef DEBUG_STAT_CACHE +#ifdef DEBUG_STAT_CACHE ctrl.size = 0; #endif @@ -122,24 +121,24 @@ stat_cache *stat_cache_init(void) { static stat_cache_entry * stat_cache_entry_init(void) { stat_cache_entry *sce = NULL; - + sce = calloc(1, sizeof(*sce)); - + sce->name = buffer_init(); sce->etag = buffer_init(); sce->content_type = buffer_init(); - + return sce; } static void stat_cache_entry_free(void *data) { stat_cache_entry *sce = data; if (!sce) return; - + buffer_free(sce->etag); buffer_free(sce->name); buffer_free(sce->content_type); - + free(sce); } @@ -148,22 +147,22 @@ static fam_dir_entry * fam_dir_entry_init(void) { fam_dir_entry *fam_dir = NULL; fam_dir = calloc(1, sizeof(*fam_dir)); - + fam_dir->name = buffer_init(); - + return fam_dir; } static void fam_dir_entry_free(void *data) { fam_dir_entry *fam_dir = data; - + if (!fam_dir) return; - + FAMCancelMonitor(fam_dir->fc, fam_dir->req); - + buffer_free(fam_dir->name); free(fam_dir->req); - + free(fam_dir); } #endif @@ -174,7 +173,7 @@ void stat_cache_free(stat_cache *sc) { splay_tree *node = sc->files; osize = sc->files->size; - + stat_cache_entry_free(node->data); sc->files = splaytree_delete(sc->files, node->key); @@ -182,17 +181,18 @@ void stat_cache_free(stat_cache *sc) { } buffer_free(sc->dir_name); + buffer_free(sc->hash_key); #ifdef HAVE_FAM_H while (sc->dirs) { int osize; splay_tree *node = sc->dirs; - + osize = sc->dirs->size; fam_dir_entry_free(node->data); sc->dirs = splaytree_delete(sc->dirs, node->key); - + if (osize == 1) { assert(NULL == sc->dirs); } else { @@ -212,7 +212,7 @@ void stat_cache_free(stat_cache *sc) { static int stat_cache_attr_get(buffer *buf, char *name) { int attrlen; int ret; - + attrlen = 1024; buffer_prepare_copy(buf, attrlen); attrlen--; @@ -238,9 +238,8 @@ static uint32_t hashme(buffer *str) { } #ifdef HAVE_FAM_H -handler_t stat_cache_handle_fdevent(void *_srv, void *_fce, int revent) { +handler_t stat_cache_handle_fdevent(server *srv, void *_fce, int revent) { size_t i; - server *srv = _srv; stat_cache *sc = srv->stat_cache; size_t events; @@ -251,15 +250,15 @@ handler_t stat_cache_handle_fdevent(void *_srv, void *_fce, int revent) { sc->fam) { events = FAMPending(sc->fam); - + for (i = 0; i < events; i++) { FAMEvent fe; fam_dir_entry *fam_dir; splay_tree *node; - int ndx; - + int ndx, j; + FAMNextEvent(sc->fam, &fe); - + /* handle event */ switch(fe.code) { @@ -274,20 +273,25 @@ handler_t stat_cache_handle_fdevent(void *_srv, void *_fce, int revent) { /* file/dir is still here */ if (fe.code == FAMChanged) break; - buffer_copy_string(sc->dir_name, fe.filename); + /* we have 2 versions, follow and no-follow-symlink */ + + for (j = 0; j < 2; j++) { + buffer_copy_string(sc->hash_key, fe.filename); + buffer_append_long(sc->hash_key, j); - ndx = hashme(sc->dir_name); + ndx = hashme(sc->hash_key); - sc->dirs = splaytree_splay(sc->dirs, ndx); - node = sc->dirs; - - if (node && (node->key == ndx)) { - int osize = splaytree_size(sc->dirs); + sc->dirs = splaytree_splay(sc->dirs, ndx); + node = sc->dirs; - fam_dir_entry_free(node->data); - sc->dirs = splaytree_delete(sc->dirs, ndx); + if (node && (node->key == ndx)) { + int osize = splaytree_size(sc->dirs); - assert(osize - 1 == splaytree_size(sc->dirs)); + fam_dir_entry_free(node->data); + sc->dirs = splaytree_delete(sc->dirs, ndx); + + assert(osize - 1 == splaytree_size(sc->dirs)); + } } break; default: @@ -308,7 +312,7 @@ handler_t stat_cache_handle_fdevent(void *_srv, void *_fce, int revent) { sc->fam = NULL; } - + return HANDLER_GO_ON; } @@ -328,11 +332,25 @@ static int buffer_copy_dirname(buffer *dst, buffer *file) { } #endif +#ifdef HAVE_LSTAT +static int stat_cache_lstat(server *srv, buffer *dname, struct stat *lst) { + if (lstat(dname->ptr, lst) == 0) { + return S_ISLNK(lst->st_mode) ? 0 : 1; + } + else { + log_error_write(srv, __FILE__, __LINE__, "sbs", + "lstat failed for:", + dname, strerror(errno)); + }; + return -1; +} +#endif + /*** * * * - * returns: + * returns: * - HANDLER_FINISHED on cache-miss (don't forget to reopen the file) * - HANDLER_ERROR on stat() failed -> see errno for problem */ @@ -348,41 +366,45 @@ handler_t stat_cache_get_entry(server *srv, connection *con, buffer *name, stat_ struct stat st; size_t k; int fd; -#ifdef DEBUG_STAT_CACHE + struct stat lst; +#ifdef DEBUG_STAT_CACHE size_t i; #endif int file_ndx; splay_tree *file_node = NULL; - *ret_sce = NULL; + *ret_sce = NULL; - /* + /* * check if the directory for this file has changed */ sc = srv->stat_cache; - file_ndx = hashme(name); + buffer_copy_string_buffer(sc->hash_key, name); + buffer_append_long(sc->hash_key, con->conf.follow_symlink); + + file_ndx = hashme(sc->hash_key); sc->files = splaytree_splay(sc->files, file_ndx); -#ifdef DEBUG_STAT_CACHE +#ifdef DEBUG_STAT_CACHE for (i = 0; i < ctrl.used; i++) { if (ctrl.ptr[i] == file_ndx) break; } #endif if (sc->files && (sc->files->key == file_ndx)) { -#ifdef DEBUG_STAT_CACHE +#ifdef DEBUG_STAT_CACHE /* it was in the cache */ assert(i < ctrl.used); #endif - - /* we have seen this file already and + + /* we have seen this file already and * don't stat() it again in the same second */ file_node = sc->files; - + sce = file_node->data; /* check if the name is the same, we might have a collision */ @@ -390,7 +412,7 @@ handler_t stat_cache_get_entry(server *srv, connection *con, buffer *name, stat_ if (buffer_is_equal(name, sce->name)) { if (srv->srvconf.stat_cache_engine == STAT_CACHE_ENGINE_SIMPLE) { if (sce->stat_ts == srv->cur_ts) { - *ret_sce = sce; + *ret_sce = sce; return HANDLER_GO_ON; } } @@ -400,17 +422,18 @@ handler_t stat_cache_get_entry(server *srv, connection *con, buffer *name, stat_ * file_node is used by the FAM check below to see if we know this file * and if we can save a stat(). * - * BUT, the sce is not reset here as the entry into the cache is ok, we + * BUT, the sce is not reset here as the entry into the cache is ok, we * it is just not pointing to our requested file. - * + * * */ file_node = NULL; } } else { -#ifdef DEBUG_STAT_CACHE +#ifdef DEBUG_STAT_CACHE if (i != ctrl.used) { - fprintf(stderr, "%s.%d: %08x was already inserted but not found in cache, %s\n", __FILE__, __LINE__, file_ndx, name->ptr); + log_error_write(srv, __FILE__, __LINE__, "xSB", + file_ndx, "was already inserted but not found in cache, ", name); } assert(i == ctrl.used); #endif @@ -420,27 +443,32 @@ handler_t stat_cache_get_entry(server *srv, connection *con, buffer *name, stat_ /* dir-check */ if (srv->srvconf.stat_cache_engine == STAT_CACHE_ENGINE_FAM) { if (0 != buffer_copy_dirname(sc->dir_name, name)) { - SEGFAULT(); + log_error_write(srv, __FILE__, __LINE__, "sb", + "no '/' found in filename:", name); + return HANDLER_ERROR; } - dir_ndx = hashme(sc->dir_name); - + buffer_copy_string_buffer(sc->hash_key, sc->dir_name); + buffer_append_long(sc->hash_key, con->conf.follow_symlink); + + dir_ndx = hashme(sc->hash_key); + sc->dirs = splaytree_splay(sc->dirs, dir_ndx); - + if (sc->dirs && (sc->dirs->key == dir_ndx)) { dir_node = sc->dirs; } - + if (dir_node && file_node) { /* we found a file */ - + sce = file_node->data; fam_dir = dir_node->data; - + if (fam_dir->version == sce->dir_version) { /* the stat()-cache entry is still ok */ - - *ret_sce = sce; + + *ret_sce = sce; return HANDLER_GO_ON; } } @@ -448,18 +476,23 @@ handler_t stat_cache_get_entry(server *srv, connection *con, buffer *name, stat_ #endif /* - * *lol* + * *lol* * - open() + fstat() on a named-pipe results in a (intended) hang. - * - stat() if regualar file + open() to see if we can read from it is better + * - stat() if regular file + open() to see if we can read from it is better * * */ - if (-1 == stat(name->ptr, &st)) { return HANDLER_ERROR; } if (S_ISREG(st.st_mode)) { + /* fix broken stat/open for symlinks to reg files with appended slash on freebsd,osx */ + if (name->ptr[name->used-2] == '/') { + errno = ENOTDIR; + return HANDLER_ERROR; + } + /* try to open the file to check if we can read it */ if (-1 == (fd = open(name->ptr, O_RDONLY))) { return HANDLER_ERROR; @@ -468,17 +501,15 @@ handler_t stat_cache_get_entry(server *srv, connection *con, buffer *name, stat_ } if (NULL == sce) { - int osize = 0; - - if (sc->files) { - osize = sc->files->size; - } +#ifdef DEBUG_STAT_CACHE + int osize = splaytree_size(sc->files); +#endif sce = stat_cache_entry_init(); buffer_copy_string_buffer(sce->name, name); - - sc->files = splaytree_insert(sc->files, file_ndx, sce); -#ifdef DEBUG_STAT_CACHE + + sc->files = splaytree_insert(sc->files, file_ndx, sce); +#ifdef DEBUG_STAT_CACHE if (ctrl.size == 0) { ctrl.size = 16; ctrl.used = 0; @@ -499,47 +530,100 @@ handler_t stat_cache_get_entry(server *srv, connection *con, buffer *name, stat_ sce->st = st; sce->stat_ts = srv->cur_ts; - /* catch the obvious symlinks + /* catch the obvious symlinks * * this is not a secure check as we still have a race-condition between - * the stat() and the open. We can only solve this by + * the stat() and the open. We can only solve this by * 1. open() the file * 2. fstat() the fd * * and keeping the file open for the rest of the time. But this can * only be done at network level. - * + * + * per default it is not a symlink * */ - if (S_ISLNK(st.st_mode) && !con->conf.follow_symlink) { - return HANDLER_ERROR; - } +#ifdef HAVE_LSTAT + sce->is_symlink = 0; + + /* we want to only check for symlinks if we should block symlinks. + */ + if (!con->conf.follow_symlink) { + if (stat_cache_lstat(srv, name, &lst) == 0) { +#ifdef DEBUG_STAT_CACHE + log_error_write(srv, __FILE__, __LINE__, "sb", + "found symlink", name); +#endif + sce->is_symlink = 1; + } + + /* + * we assume "/" can not be symlink, so + * skip the symlink stuff if our path is / + **/ + else if ((name->used > 2)) { + buffer *dname; + char *s_cur; + + dname = buffer_init(); + buffer_copy_string_buffer(dname, name); + + while ((s_cur = strrchr(dname->ptr,'/'))) { + *s_cur = '\0'; + dname->used = s_cur - dname->ptr + 1; + if (dname->ptr == s_cur) { +#ifdef DEBUG_STAT_CACHE + log_error_write(srv, __FILE__, __LINE__, "s", "reached /"); +#endif + break; + } +#ifdef DEBUG_STAT_CACHE + log_error_write(srv, __FILE__, __LINE__, "sbs", + "checking if", dname, "is a symlink"); +#endif + if (stat_cache_lstat(srv, dname, &lst) == 0) { + sce->is_symlink = 1; +#ifdef DEBUG_STAT_CACHE + log_error_write(srv, __FILE__, __LINE__, "sb", + "found symlink", dname); +#endif + break; + }; + }; + buffer_free(dname); + }; + }; +#endif - if (S_ISREG(st.st_mode)) { + if (S_ISREG(st.st_mode)) { /* determine mimetype */ buffer_reset(sce->content_type); - - for (k = 0; k < con->conf.mimetypes->used; k++) { - data_string *ds = (data_string *)con->conf.mimetypes->data[k]; - buffer *type = ds->key; - - if (type->used == 0) continue; - - /* check if the right side is the same */ - if (type->used > name->used) continue; - - if (0 == strncasecmp(name->ptr + name->used - type->used, type->ptr, type->used - 1)) { - buffer_copy_string_buffer(sce->content_type, ds->value); - break; - } - } - etag_create(sce->etag, &(sce->st)); #ifdef HAVE_XATTR - if (buffer_is_empty(sce->content_type)) { + if (con->conf.use_xattr) { stat_cache_attr_get(sce->content_type, name->ptr); } #endif + /* xattr did not set a content-type. ask the config */ + if (buffer_is_empty(sce->content_type)) { + for (k = 0; k < con->conf.mimetypes->used; k++) { + data_string *ds = (data_string *)con->conf.mimetypes->data[k]; + buffer *type = ds->key; + + if (type->used == 0) continue; + + /* check if the right side is the same */ + if (type->used > name->used) continue; + + if (0 == strncasecmp(name->ptr + name->used - type->used, type->ptr, type->used - 1)) { + buffer_copy_string_buffer(sce->content_type, ds->value); + break; + } + } + } + etag_create(sce->etag, &(sce->st), con->etag_flags); + } else if (S_ISDIR(st.st_mode)) { + etag_create(sce->etag, &(sce->st), con->etag_flags); } - + #ifdef HAVE_FAM_H if (sc->fam && (srv->srvconf.stat_cache_engine == STAT_CACHE_ENGINE_FAM)) { @@ -549,19 +633,20 @@ handler_t stat_cache_get_entry(server *srv, connection *con, buffer *name, stat_ fam_dir->fc = sc->fam; buffer_copy_string_buffer(fam_dir->name, sc->dir_name); - + fam_dir->version = 1; - + fam_dir->req = calloc(1, sizeof(FAMRequest)); - - if (0 != FAMMonitorDirectory(sc->fam, fam_dir->name->ptr, + + if (0 != FAMMonitorDirectory(sc->fam, fam_dir->name->ptr, fam_dir->req, fam_dir)) { - - log_error_write(srv, __FILE__, __LINE__, "sbs", - "monitoring dir failed:", + + log_error_write(srv, __FILE__, __LINE__, "sbsbs", + "monitoring dir failed:", fam_dir->name, + "file:", name, FamErrlist[FAMErrno]); - + fam_dir_entry_free(fam_dir); } else { int osize = 0; @@ -570,7 +655,7 @@ handler_t stat_cache_get_entry(server *srv, connection *con, buffer *name, stat_ osize = sc->dirs->size; } - sc->dirs = splaytree_insert(sc->dirs, dir_ndx, fam_dir); + sc->dirs = splaytree_insert(sc->dirs, dir_ndx, fam_dir); assert(sc->dirs); assert(sc->dirs->data == fam_dir); assert(osize == (sc->dirs->size - 1)); @@ -578,9 +663,9 @@ handler_t stat_cache_get_entry(server *srv, connection *con, buffer *name, stat_ } else { fam_dir = dir_node->data; } - + /* bind the fam_fc to the stat() cache entry */ - + if (fam_dir) { sce->dir_version = fam_dir->version; sce->dir_ndx = dir_ndx; @@ -594,11 +679,11 @@ handler_t stat_cache_get_entry(server *srv, connection *con, buffer *name, stat_ } /** - * remove stat() from cache which havn't been stat()ed for + * remove stat() from cache which havn't been stat()ed for * more than 10 seconds - * * - * walk though the stat-cache, collect the ids which are too old + * + * walk though the stat-cache, collect the ids which are too old * and remove them in a second loop */ @@ -639,9 +724,9 @@ int stat_cache_trigger_cleanup(server *srv) { sc->files = splaytree_splay(sc->files, ndx); node = sc->files; - + if (node && (node->key == ndx)) { -#ifdef DEBUG_STAT_CACHE +#ifdef DEBUG_STAT_CACHE size_t j; int osize = splaytree_size(sc->files); stat_cache_entry *sce = node->data; @@ -649,7 +734,7 @@ int stat_cache_trigger_cleanup(server *srv) { stat_cache_entry_free(node->data); sc->files = splaytree_delete(sc->files, ndx); -#ifdef DEBUG_STAT_CACHE +#ifdef DEBUG_STAT_CACHE for (j = 0; j < ctrl.used; j++) { if (ctrl.ptr[j] == ndx) { ctrl.ptr[j] = ctrl.ptr[--ctrl.used]; diff --git a/src/stat_cache.h b/src/stat_cache.h index e6f7796..7b80cce 100644 --- a/src/stat_cache.h +++ b/src/stat_cache.h @@ -7,7 +7,7 @@ stat_cache *stat_cache_init(void); void stat_cache_free(stat_cache *fc); handler_t stat_cache_get_entry(server *srv, connection *con, buffer *name, stat_cache_entry **fce); -handler_t stat_cache_handle_fdevent(void *_srv, void *_fce, int revent); +handler_t stat_cache_handle_fdevent(server *srv, void *_fce, int revent); int stat_cache_trigger_cleanup(server *srv); #endif diff --git a/src/status_counter.c b/src/status_counter.c new file mode 100644 index 0000000..ab130da --- /dev/null +++ b/src/status_counter.c @@ -0,0 +1,61 @@ +#include "status_counter.h" + +#include <stdlib.h> + +/** + * The status array can carry all the status information you want + * the key to the array is <module-prefix>.<name> + * and the values are counters + * + * example: + * fastcgi.backends = 10 + * fastcgi.active-backends = 6 + * fastcgi.backend.<key>.load = 24 + * fastcgi.backend.<key>.... + * + * fastcgi.backend.<key>.disconnects = ... + */ + +data_integer *status_counter_get_counter(server *srv, const char *s, size_t len) { + data_integer *di; + + if (NULL == (di = (data_integer *)array_get_element(srv->status, s))) { + /* not found, create it */ + + if (NULL == (di = (data_integer *)array_get_unused_element(srv->status, TYPE_INTEGER))) { + di = data_integer_init(); + } + buffer_copy_string_len(di->key, s, len); + di->value = 0; + + array_insert_unique(srv->status, (data_unset *)di); + } + return di; +} + +/* dummies of the statistic framework functions + * they will be moved to a statistics.c later */ +int status_counter_inc(server *srv, const char *s, size_t len) { + data_integer *di = status_counter_get_counter(srv, s, len); + + di->value++; + + return 0; +} + +int status_counter_dec(server *srv, const char *s, size_t len) { + data_integer *di = status_counter_get_counter(srv, s, len); + + if (di->value > 0) di->value--; + + return 0; +} + +int status_counter_set(server *srv, const char *s, size_t len, int val) { + data_integer *di = status_counter_get_counter(srv, s, len); + + di->value = val; + + return 0; +} + diff --git a/src/status_counter.h b/src/status_counter.h new file mode 100644 index 0000000..ba5e76f --- /dev/null +++ b/src/status_counter.h @@ -0,0 +1,14 @@ +#ifndef _STATUS_COUNTER_H_ +#define _STATUS_COUNTER_H_ + +#include "array.h" +#include "base.h" + +#include <sys/types.h> + +data_integer *status_counter_get_counter(server *srv, const char *s, size_t len); +int status_counter_inc(server *srv, const char *s, size_t len); +int status_counter_dec(server *srv, const char *s, size_t len); +int status_counter_set(server *srv, const char *s, size_t len, int val); + +#endif diff --git a/src/stream.c b/src/stream.c index ecaadc1..c29a5ca 100644 --- a/src/stream.c +++ b/src/stream.c @@ -1,14 +1,11 @@ +#include "stream.h" + #include <sys/types.h> #include <sys/stat.h> -#include <unistd.h> +#include <unistd.h> #include <fcntl.h> -#include "stream.h" -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - #include "sys-mmap.h" #ifndef O_BINARY @@ -25,33 +22,33 @@ int stream_open(stream *f, buffer *fn) { #endif f->start = NULL; - + if (-1 == stat(fn->ptr, &st)) { return -1; } - + f->size = st.st_size; #ifdef HAVE_MMAP if (-1 == (fd = open(fn->ptr, O_RDONLY | O_BINARY))) { return -1; } - - f->start = mmap(0, f->size, PROT_READ, MAP_SHARED, fd, 0); - + + f->start = mmap(NULL, f->size, PROT_READ, MAP_SHARED, fd, 0); + close(fd); - + if (MAP_FAILED == f->start) { return -1; } #elif defined __WIN32 - fh = CreateFile(fn->ptr, - GENERIC_READ, - FILE_SHARE_READ, - NULL, - OPEN_EXISTING, - FILE_ATTRIBUTE_READONLY, + fh = CreateFile(fn->ptr, + GENERIC_READ, + FILE_SHARE_READ, + NULL, + OPEN_EXISTING, + FILE_ATTRIBUTE_READONLY, NULL); if (!fh) return -1; @@ -64,19 +61,20 @@ int stream_open(stream *f, buffer *fn) { NULL); if (!mh) { +/* LPVOID lpMsgBuf; FormatMessage( - FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) &lpMsgBuf, 0, NULL ); - +*/ return -1; } - + p = MapViewOfFile(mh, FILE_MAP_READ, 0, @@ -87,9 +85,9 @@ int stream_open(stream *f, buffer *fn) { f->start = p; #else -# error no mmap found +# error no mmap found #endif - + return 0; } diff --git a/src/sys-socket.h b/src/sys-socket.h index cc6e649..f35699d 100644 --- a/src/sys-socket.h +++ b/src/sys-socket.h @@ -8,6 +8,7 @@ #define ECONNRESET WSAECONNRESET #define EINPROGRESS WSAEINPROGRESS #define EALREADY WSAEALREADY +#define ECONNABORTED WSAECONNABORTED #define ioctl ioctlsocket #define hstrerror(x) "" #else diff --git a/src/version.h b/src/version.h new file mode 100644 index 0000000..e05b654 --- /dev/null +++ b/src/version.h @@ -0,0 +1,12 @@ +#ifndef _VERSION_H_ +#define _VERSION_H_ + +#ifdef HAVE_VERSION_H +# include "versionstamp.h" +#else +# define REPO_VERSION "" +#endif + +#define PACKAGE_DESC PACKAGE_NAME "/" PACKAGE_VERSION REPO_VERSION + +#endif |