61 files changed, 7102 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e941fe5 --- a/dev/null +++ b/.gitignore @@ -0,0 +1,23 @@ +configure +Makefile.in +Doxyfile +config.log +depcomp +config.guess +config.h +ltmain.sh +config.sub +INSTALL +sitecing.pc +NEWS +Makefile +config.status +stamp-h1 +doxydox +config.h.in +autom4te.cache +libtool +missing +aclocal.m4 +ylwrap +install-sh @@ -0,0 +1,3 @@ +Klever dissected: + Michael 'hacker' Krelin <hacker@klever.net> + Leonid Ivanov <kamel@klever.net> @@ -0,0 +1,19 @@ +Copyright (c) 2004-2005 Klever Group (http://www.klever.net/) + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/ChangeLog b/ChangeLog new file mode 100644 index 0000000..e69de29 --- a/dev/null +++ b/ChangeLog diff --git a/Doxyfile.in b/Doxyfile.in new file mode 100644 index 0000000..b6e7ed2 --- a/dev/null +++ b/Doxyfile.in @@ -0,0 +1,247 @@ +# Doxyfile 1.3.9.1 + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- +PROJECT_NAME = @PACKAGE@ +PROJECT_NUMBER = @VERSION@ +OUTPUT_DIRECTORY = @builddir@/doxydox +CREATE_SUBDIRS = NO +OUTPUT_LANGUAGE = English +USE_WINDOWS_ENCODING = NO +BRIEF_MEMBER_DESC = YES +REPEAT_BRIEF = YES +ABBREVIATE_BRIEF = +ALWAYS_DETAILED_SEC = NO +INLINE_INHERITED_MEMB = NO +FULL_PATH_NAMES = YES +STRIP_FROM_PATH = include +STRIP_FROM_INC_PATH = include +SHORT_NAMES = NO +JAVADOC_AUTOBRIEF = NO +MULTILINE_CPP_IS_BRIEF = NO +DETAILS_AT_TOP = NO +INHERIT_DOCS = YES +DISTRIBUTE_GROUP_DOC = NO +TAB_SIZE = 8 +ALIASES = +OPTIMIZE_OUTPUT_FOR_C = NO +OPTIMIZE_OUTPUT_JAVA = NO +SUBGROUPING = YES + +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- + +EXTRACT_ALL = NO +EXTRACT_PRIVATE = NO +EXTRACT_STATIC = NO +EXTRACT_LOCAL_CLASSES = YES +EXTRACT_LOCAL_METHODS = NO +HIDE_UNDOC_MEMBERS = NO +HIDE_UNDOC_CLASSES = NO +HIDE_FRIEND_COMPOUNDS = NO +HIDE_IN_BODY_DOCS = NO +INTERNAL_DOCS = NO +CASE_SENSE_NAMES = YES +HIDE_SCOPE_NAMES = NO +SHOW_INCLUDE_FILES = NO +INLINE_INFO = YES +SORT_MEMBER_DOCS = YES +SORT_BRIEF_DOCS = NO +SORT_BY_SCOPE_NAME = YES +GENERATE_TODOLIST = YES +GENERATE_TESTLIST = YES +GENERATE_BUGLIST = YES +GENERATE_DEPRECATEDLIST= YES +ENABLED_SECTIONS = +MAX_INITIALIZER_LINES = 30 +SHOW_USED_FILES = NO +SHOW_DIRECTORIES = YES + +#--------------------------------------------------------------------------- +# configuration options related to warning and progress messages +#--------------------------------------------------------------------------- + +QUIET = NO +WARNINGS = YES +WARN_IF_UNDOCUMENTED = YES +WARN_IF_DOC_ERROR = YES +WARN_FORMAT = "$file:$line: $text" +WARN_LOGFILE = + +#--------------------------------------------------------------------------- +# configuration options related to the input files +#--------------------------------------------------------------------------- + +INPUT = \ + @srcdir@/include/sitecing/ +FILE_PATTERNS = *.h +RECURSIVE = NO +EXCLUDE = +EXCLUDE_SYMLINKS = NO +EXCLUDE_PATTERNS = +EXAMPLE_PATH = +EXAMPLE_PATTERNS = +EXAMPLE_RECURSIVE = NO +IMAGE_PATH = +INPUT_FILTER = +FILTER_PATTERNS = +FILTER_SOURCE_FILES = NO + +#--------------------------------------------------------------------------- +# configuration options related to source browsing +#--------------------------------------------------------------------------- + +SOURCE_BROWSER = NO +INLINE_SOURCES = NO +STRIP_CODE_COMMENTS = YES +REFERENCED_BY_RELATION = YES +REFERENCES_RELATION = YES +VERBATIM_HEADERS = YES + +#--------------------------------------------------------------------------- +# configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- + +ALPHABETICAL_INDEX = YES +COLS_IN_ALPHA_INDEX = 2 +IGNORE_PREFIX = + +#--------------------------------------------------------------------------- +# configuration options related to the HTML output +#--------------------------------------------------------------------------- + +GENERATE_HTML = YES +HTML_OUTPUT = html +HTML_FILE_EXTENSION = .html +HTML_HEADER = +HTML_FOOTER = +HTML_STYLESHEET = +HTML_ALIGN_MEMBERS = YES +GENERATE_HTMLHELP = NO +CHM_FILE = +HHC_LOCATION = +GENERATE_CHI = NO +BINARY_TOC = NO +TOC_EXPAND = NO +DISABLE_INDEX = NO +ENUM_VALUES_PER_LINE = 4 +GENERATE_TREEVIEW = NO +TREEVIEW_WIDTH = 250 + +#--------------------------------------------------------------------------- +# configuration options related to the LaTeX output +#--------------------------------------------------------------------------- + +GENERATE_LATEX = NO +LATEX_OUTPUT = latex +LATEX_CMD_NAME = latex +MAKEINDEX_CMD_NAME = makeindex +COMPACT_LATEX = NO +PAPER_TYPE = a4wide +EXTRA_PACKAGES = +LATEX_HEADER = +PDF_HYPERLINKS = NO +USE_PDFLATEX = NO +LATEX_BATCHMODE = NO +LATEX_HIDE_INDICES = NO + +#--------------------------------------------------------------------------- +# configuration options related to the RTF output +#--------------------------------------------------------------------------- + +GENERATE_RTF = NO +RTF_OUTPUT = rtf +COMPACT_RTF = NO +RTF_HYPERLINKS = NO +RTF_STYLESHEET_FILE = +RTF_EXTENSIONS_FILE = + +#--------------------------------------------------------------------------- +# configuration options related to the man page output +#--------------------------------------------------------------------------- + +GENERATE_MAN = NO +MAN_OUTPUT = man +MAN_EXTENSION = .3 +MAN_LINKS = YES + +#--------------------------------------------------------------------------- +# configuration options related to the XML output +#--------------------------------------------------------------------------- + +GENERATE_XML = YES +XML_OUTPUT = xml +XML_SCHEMA = +XML_DTD = +XML_PROGRAMLISTING = YES + +#--------------------------------------------------------------------------- +# configuration options for the AutoGen Definitions output +#--------------------------------------------------------------------------- + +GENERATE_AUTOGEN_DEF = NO + +#--------------------------------------------------------------------------- +# configuration options related to the Perl module output +#--------------------------------------------------------------------------- + +GENERATE_PERLMOD = NO +PERLMOD_LATEX = NO +PERLMOD_PRETTY = YES +PERLMOD_MAKEVAR_PREFIX = + +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- + +ENABLE_PREPROCESSING = YES +MACRO_EXPANSION = NO +EXPAND_ONLY_PREDEF = NO +SEARCH_INCLUDES = YES +INCLUDE_PATH = +INCLUDE_FILE_PATTERNS = +PREDEFINED = +EXPAND_AS_DEFINED = +SKIP_FUNCTION_MACROS = YES + +#--------------------------------------------------------------------------- +# Configuration::additions related to external references +#--------------------------------------------------------------------------- + +TAGFILES = +GENERATE_TAGFILE = +ALLEXTERNALS = NO +EXTERNAL_GROUPS = YES +PERL_PATH = /usr/bin/perl + +#--------------------------------------------------------------------------- +# Configuration options related to the dot tool +#--------------------------------------------------------------------------- + +CLASS_DIAGRAMS = YES +HIDE_UNDOC_RELATIONS = YES +HAVE_DOT = @HAVE_DOT@ +CLASS_GRAPH = YES +COLLABORATION_GRAPH = YES +UML_LOOK = NO +TEMPLATE_RELATIONS = YES +INCLUDE_GRAPH = YES +INCLUDED_BY_GRAPH = YES +CALL_GRAPH = NO +GRAPHICAL_HIERARCHY = YES +DOT_IMAGE_FORMAT = png +DOT_PATH = @DOT@ +DOTFILE_DIRS = +MAX_DOT_GRAPH_WIDTH = 1024 +MAX_DOT_GRAPH_HEIGHT = 1024 +MAX_DOT_GRAPH_DEPTH = 0 +GENERATE_LEGEND = YES +DOT_CLEANUP = YES + +#--------------------------------------------------------------------------- +# Configuration::additions related to the search engine +#--------------------------------------------------------------------------- + +SEARCHENGINE = NO diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 0000000..68f550d --- a/dev/null +++ b/Makefile.am @@ -0,0 +1,27 @@ +SUBDIRS=include lib share src components +EXTRA_DIST= NEWS NEWS.xml NEWS.xsl + +DISTCHECK_CONFIGURE_FLAGS=--with-pkgconfigdir=$${dc_install_base}/lib/pkgconfig +if HAVE_PKGCONFIG +pkgconfigdir=@PKGCONFIG_DIR@ +pkgconfig_DATA=sitecing.pc +endif + +LOCAL_TARGETS= +if HAVE_DOXYGEN +LOCAL_TARGETS+=doxygen +endif + +all-local: NEWS $(addprefix all-lota-,${LOCAL_TARGETS}) +clean-local: $(addprefix clean-lota-,${LOCAL_TARGETS}) + +NEWS: NEWS.xsl NEWS.xml + ${XSLTPROC} -o $@ NEWS.xsl NEWS.xml + +all-lota-doxygen: doxydox/built +doxydox/built: $(wildcard ${top_srcdir}/include/sitecing/*.h) + ${DOXYGEN} + touch $@ + +clean-lota-doxygen: + rm -rf doxydox diff --git a/NEWS.xml b/NEWS.xml new file mode 100644 index 0000000..d1d89f6 --- a/dev/null +++ b/NEWS.xml @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="us-ascii"?> +<news> + <version version="0.0" date="January 29th, 2005"> + <ni>Initial release</ni> + </version> +</news> diff --git a/NEWS.xsl b/NEWS.xsl new file mode 100644 index 0000000..7c71307 --- a/dev/null +++ b/NEWS.xsl @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="us-ascii"?> +<xsl:stylesheet version="1.0" + xmlns:xsl="http://www.w3.org/1999/XSL/Transform" + > + <xsl:output + method="text" + encoding="us-ascii" + media-type="text/plain" /> + + <xsl:template match="news"> + <xsl:apply-templates/> + </xsl:template> + <xsl:template match="version"> + <xsl:value-of select="concat(@version,' (',@date,')
')"/> + <xsl:apply-templates/> + </xsl:template> + <xsl:template match="ni"> + <xsl:text> - </xsl:text> + <xsl:apply-templates mode="text"/> + <xsl:text>
</xsl:text> + </xsl:template> + <xsl:template match="*|text()"/> + +</xsl:stylesheet> diff --git a/acinclude.m4 b/acinclude.m4 new file mode 100644 index 0000000..14b6de7 --- a/dev/null +++ b/acinclude.m4 @@ -0,0 +1,211 @@ +dnl AC_WITH_PKGCONFIG([ACTION-IF-FOUND[,ACTION-IF-NOT-FOUND]]) +dnl Outputs: +dnl AC_SUBST: PKGCONFIG_PKGCONFIG PKGCONFIG_DIR +dnl AM_CONDTIONAL: HAVE_PKGCONFIG +AC_DEFUN([AC_WITH_PKGCONFIG],[ + PKGCONFIG_PKGCONFIG="" + PKGCONFIG_DIR="" + HAVE_PKGCONFIG="no" + EXPLICIT_PKGCONFIGDIR="no" + test -z "${WANT_PKGCONFIG}" && WANT_PKGCONFIG="" + AC_PATH_PROG([PKGCONFIG_PKGCONFIG],[pkg-config],[false]) + if test "${PKGCONFIG_PKGCONFIG}" != "false" ; then + AC_ARG_WITH([pkgconfigdir], + AC_HELP_STRING([--with-pkgconfigdir=dir],[Specify pkgconfig directory]), + [ + if test "${withval}" = "no" ; then + WANT_PKGCONFIG="no" + else + PKGCONFIG_DIR="${withval}" + EXPLICIT_PKGCONFIGDIR="yes" + fi + ],[ + AC_MSG_CHECKING([for pkgconfig directory]) + PKGCONFIG_DIR="`${PKGCONFIG_PKGCONFIG} --debug 2>&1 | grep '^Scanning'| head -n 1 | cut -d\' -f2-|cut -d\' -f1`" + AC_MSG_RESULT([${PKGCONFIG_DIR}]) + ] + ) + if test -d "${PKGCONFIG_DIR}" ; then + HAVE_PKGCONFIG=yes + AC_SUBST([PKGCONFIG_PKGCONFIG]) + AC_SUBST([PKGCONFIG_DIR]) + else + AC_MSG_NOTICE([unexistent pkgconfig directory: ${PKGCONFIG_DIR}]) + if test "${EXPLICIT_PKGCONFIGDIR}" = "yes" ; then + HAVE_PKGCONFIG=yes + AC_SUBST([PKGCONFIG_PKGCONFIG]) + AC_SUBST([PKGCONFIG_DIR]) + else + ifelse([$2], , :, [$2]) + fi + fi + fi + AM_CONDITIONAL([HAVE_PKGCONFIG],[test "${HAVE_PKGCONFIG}" = "yes"]) +]) + +dnl AC_WITH_DOXYGEN([ACTION-IF-FOUND[,ACTION-IF-NOT-FOUND]]) +dnl Outputs: +dnl AC_SUBST: DOXYGEN HAVE_DOXYGEN +dnl AM_CONDTIONAL: HAVE_DOXYGEN +AC_DEFUN([AC_WITH_DOXYGEN],[ + HAVE_DOXYGEN="no" + AC_PATH_PROG([DOXYGEN],[doxygen],[false]) + if test "${DOXYGEN}" = "false" ; then + ifelse([$2], , :, [$2]) + else + HAVE_DOXYGEN="yes" + AC_SUBST([DOXYGEN]) + $1 + fi + AC_SUBST([HAVE_DOXYGEN]) + AM_CONDITIONAL([HAVE_DOXYGEN],[test "${HAVE_DOXYGEN}" = "yes"]) +]) + +dnl AC_WITH_DOT([ACTION-IF-FOUND[,ACTION-IF-NOT-FOUND]]) +dnl Outputs: +dnl AC_SUBST: DOT HAVE_DOT +dnl AM_CONDITIONAL: HAVE_DOT +AC_DEFUN([AC_WITH_DOT],[ + HAVE_DOT="no" + AC_PATH_PROG([DOT],[dot],[false]) + if test "${DOT}" = "false" ; then + ifelse([$2], , :, [$2]) + else + HAVE_DOT="yes" + AC_SUBST([DOT]) + $1 + fi +AC_SUBST([HAVE_DOT]) + AM_CONDITIONAL([HAVE_DOT],[test "${HAVE_DOT}" = "yes"]) +]) + +dnl AC_WITH_PCRE([ACTION-IF-FOUND[,ACTION-IF-NOT-FOUND]]) +dnl Outputs: +dnl AC_SUBST: PCRE_CONFIG PCRE_PREFIX PCRE_EXEC_PREFIX +dnl PCRE_VERSION PCRE_CFLAGS PCRE_LIBS +dnl PCRE_LIBS_POSIX PCRE_CFLAGS_POSIX +dnl AM_CONDITIONAL: HAVE_PCRE +dnl AC_DEFINE: HAVE_PCRE PCRE_VERSION +AC_DEFUN([AC_WITH_PCRE],[ + HAVE_PCRE="no" + PCRE_CONFIG="" + PCRE_PREFIX="" + PCRE_EXEC_PREFIX="" + PCRE_VERSION="" + PCRE_CFLAGS="" + PCRE_LIBS="" + PCRE_LOCATIONS="${PATH}:/usr/local/bin:/usr/bin" + test -z "$WANT_PCRE" && WANT_PCRE="" + AC_ARG_WITH([pcre], + AC_HELP_STRING([--with-pcre=location],[Look for pcre in specified locations]), + [ + if test "${withval}" = "no" ; then + WANT_PCRE="no" + else + if test -x "${withval}" ; then + PCRE_CONFIG="${withval}" + elif test -x "${withval}/pcre-config" ; then + PCRE_CONFIG="${withval}/pcre-config" + elif test -x "${withval}/bin/pcre-config" ; then + PCRE_CONFIG="${withval}/bin/pcre-config" + fi + fi + ] + ) + if test "${WANT_PCRE}" = "no" ; then + ifelse([$2], , :, [$2]) + else + if test -z "${PCRE_CONFIG}" ; then + AC_PATH_PROG(PCRE_CONFIG,[pcre-config],false,[${PCRE_LOCATIONS}]) + if test "${PCRE_CONFIG}" = "false" ; then + ifelse([$2], , :, [$2]) + else + HAVE_PCRE="yes" + PCRE_PREFIX="`${PCRE_CONFIG} --prefix`" + PCRE_EXEC_PREFIX="`${PCRE_CONFIG} --exec-prefix`" + PCRE_VERSION="`${PCRE_CONFIG} --version`" + PCRE_CFLAGS="`${PCRE_CONFIG} --cflags`" + PCRE_LIBS="`${PCRE_CONFIG} --libs`" + PCRE_CFLAGS_POSIX="`${PCRE_CONFIG} --cflags-posix`" + PCRE_LIBS_POSIX="`${PCRE_CONFIG} --libs-posix`" + AC_SUBST([PCRE_CONFIG]) + AC_SUBST([PCRE_PREFIX]) + AC_SUBST([PCRE_EXEC_PREFIX]) + AC_SUBST([PCRE_VERSION]) + AC_SUBST([PCRE_CFLAGS]) + AC_SUBST([PCRE_LIBS]) + AC_SUBST([PCRE_CFLAGS_POSIX]) + AC_SUBST([PCRE_LIBS_POSIX]) + AC_DEFINE([HAVE_PCRE],,[pcre support]) + AC_DEFINE_UNQUOTED([PCRE_VERSION],["${PCRE_VERSION}"],[pcre version]) + $1 + fi + fi + fi + AM_CONDITIONAL([HAVE_PCRE],[test "${HAVE_PCRE}" = "yes"]) +]) + +dnl AC_WITH_PCREPP([ACTION-IF-FOUND[,ACTION-IF-NOT-FOUND]]) +dnl Outputs: +dnl AC_SUBST: PCREPP_CONFIG PCREPP_PREFIX PCREPP_EXEC_PREFIX +dnl PCREPP_VERSION PCREPP_CFLAGS PCREPP_LIBS +dnl AM_CONDITIONAL: HAVE_PCREPP +dnl AC_DEFINE: HAVE_PCREPP PCREPP_VERSION +AC_DEFUN([AC_WITH_PCREPP],[ + HAVE_PCREPP="no" + PCREPP_CONFIG="" + PCREPP_PREFIX="" + PCREPP_EXEC_PREFIX="" + PCREPP_VERSION="" + PCREPP_CFLAGS="" + PCREPP_LIBS="" + PCREPP_LOCATIONS="${PATH}:/usr/local/bin:/usr/bin" + test -z "$WANT_PCREPP" && WANT_PCREPP="" + AC_ARG_WITH([pcre++], + AC_HELP_STRING([--with-pcre++=location],[Look for pcre++ in specified locations]), + [ + if test "${withval}" = "no" ; then + WANT_PCREPP="no" + else + if test -x "${withval}" ; then + PCREPP_CONFIG="${withval}" + elif test -x "${withval}/pcre++-config" ; then + PCREPP_CONFIG="${withval}/pcre++-config" + elif test -x "${withval}/bin/pcre++-config" ; then + PCREPP_CONFIG="${withval}/bin/pcre++-config" + fi + fi + ] + ) + if test "${WANT_PCREPP}" = "no" ; then + ifelse([$2], , :, [$2]) + else + if test "${HAVE_PCRE}" != "yes" ; then + ifelse([$2], , :, [$2]) + else + if test -z "${PCREPP_CONFIG}" ; then + AC_PATH_PROG([PCREPP_CONFIG],[pcre++-config],false,[${PCREPP_LOCATIONS}]) + if test "${PCREPP_CONFIG}" = "false" ; then + ifelse([$2], , :, [$2]) + else + HAVE_PCREPP="yes" + PCREPP_PREFIX="`${PCREPP_CONFIG} --prefix`" + PCREPP_EXEC_PREFIX="`${PCREPP_CONFIG} --exec-prefix`" + PCREPP_VERSION="`${PCREPP_CONFIG} --version`" + PCREPP_CFLAGS="`${PCREPP_CONFIG} --cflags` ${PCRE_CFLAGS}" + PCREPP_LIBS="`${PCREPP_CONFIG} --libs` ${PCRE_LIBS}" + AC_SUBST([PCREPP_CONFIG]) + AC_SUBST([PCREPP_PREFIX]) + AC_SUBST([PCREPP_EXEC_PREFIX]) + AC_SUBST([PCREPP_VERSION]) + AC_SUBST([PCREPP_CFLAGS]) + AC_SUBST([PCREPP_LIBS]) + AC_DEFINE([HAVE_PCREPP],,[pcre++ support]) + AC_DEFINE_UNQUOTED([PCREPP_VERSION],["${PCREPP_VERSION}"],[pcre++ version]) + $1 + fi + fi + fi + fi + AM_CONDITIONAL([HAVE_PCREPP],[test "${HAVE_PCREPP}" = "yes"]) +]) diff --git a/autogen.sh b/autogen.sh new file mode 100755 index 0000000..ba27501 --- a/dev/null +++ b/autogen.sh @@ -0,0 +1,9 @@ +#!/bin/sh +WANT_AUTOMAKE=1.8 +export WANT_AUTOMAKE +libtoolize -f \ +&& aclocal \ +&& autoheader \ +&& automake -a \ +&& autoconf \ +&& ./configure "$@" diff --git a/components/.gitignore b/components/.gitignore new file mode 100644 index 0000000..3dda729 --- a/dev/null +++ b/components/.gitignore @@ -0,0 +1,2 @@ +Makefile.in +Makefile diff --git a/components/Makefile.am b/components/Makefile.am new file mode 100644 index 0000000..8a07ffc --- a/dev/null +++ b/components/Makefile.am @@ -0,0 +1,5 @@ +componentsdir = ${pkgdatadir}/components + +components_DATA = exception_dev exception_prod + +EXTRA_DIST = exception_dev exception_prod diff --git a/components/exception_dev b/components/exception_dev new file mode 100644 index 0000000..d8c84e1 --- a/dev/null +++ b/components/exception_dev @@ -0,0 +1,346 @@ +%%decl using namespace std; +<%impl> + #include <iostream> + #include <fstream> + #include <sstream> + #include <cassert> + #include <cstdarg> + #include <stdexcept> + #include <cxxabi.h> + #include <sitecing/sitecing_util.h> + #include <sitecing/util.h> + #include <sitecing/magic.h> + #include <konforka/exception.h> +</%impl> +%%var string message; +%%var string root_source; +%%var string root_intermediate; +%%var string root_so; +%%var string component; +%%var int line_number = -1; +%%var const exception* exception_caught; +<%code> + __SCIF->headers.clear(); + __SCIF->out->seekp(0); + int magic = _magic; + va_list va = _args; + switch(magic) { + case sitecing::__magic_compile_error: + message = va_arg(va,const char*); + root_source = va_arg(va,const char*); + root_intermediate = va_arg(va,const char*); + root_so = va_arg(va,const char*); + component = va_arg(va,const char*); + break; + case sitecing::__magic_preprocess_error: + message = va_arg(va,const char*); + root_source = va_arg(va,const char*); + root_intermediate = va_arg(va,const char*); + root_so = va_arg(va,const char*); + component = va_arg(va,const char*); + line_number = va_arg(va,int); + break; + case sitecing::__magic_generic_exception: + message = va_arg(va,const char*); + root_source = va_arg(va,const char*); + root_intermediate = va_arg(va,const char *); + root_so = va_arg(va,const char *); + component = va_arg(va,const char*); + exception_caught = va_arg(va,const exception*); + break; + default: + break; + } +</%code> +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en"> + <head> + <title><% message %></title> + <style type="text/css"> + <!-- + body { + font-family: sans-serif; + font-size: 11pt; + } + + h1 { + font-family: serif; + font-size: 130%; + font-weight: bold; + text-align: center; + } + p { + text-indent: 2em; + text-align: justify; + } + + dl.exception-props { + margin: 1ex 1em; + padding: 0.5ex; + border: solid 1px gray; + background-color: #e0e0e0; + } + dl.exception-props dt { + font-weight: bold; + color: blue; + } + dl.exception-props dd { + color: gray; + } + + div.exception-codepoint-report { + border: solid 1px black; + margin: 0.5ex 1em 0.ex 3em; + } + div.exception-codepoint-report h3 { + display: block; + color: blue; + border-bottom: 3px double black; + padding: 0.3ex; margin: 0px; + background: #e0e0e0; + } + div.exception-codepoint-report ul { + padding: 0px; + margin: 0px; + background: #87fdff; + font-size: 70%; + } + div.exception-codepoint-report li { + font-family: monospace; + list-style-type: none; + white-space: nowrap; + overflow: hidden; + } + div.exception-codepoint-report li.focused { + color: red; + border-top: solid 1px red; border-bottom: solid 1px red; + } + div.exception-codepoint-report li .lineno { + padding-right: 0.5ex; + border-right: dotted black 1px; + } + div.exception-codepoint-report div.what { + border-top: double 3px black; + padding: 0.5ex 2em; + font-weight: bold; color: #4040c0; + overflow: auto; + } + div.backtrace div.exception-codepoint-report div.what { + color: gray; + } + + div.exception-compile div.what { + font-weight: normal; + color: red; + } + + div.powered { + margin: 2em 0px 0px 50%; + padding: 1ex 2ex; + text-align: right; + font-family: serif; + font-size: 140%; + font-weight: bold; + border-top: solid 2px black; + border-left: solid 1px gray; border-right: solid 1px gray; border-bottom: solid 1px gray; + background: #c0c0f0; + } + --> + </style> +% __SCIF->headers["Content-Type"]="text/html; charset=utf-8"; +% __SCIF->headers["Pragma"]="no-cache"; + </head> + <body> + <%code> + switch(magic) { + case sitecing::__magic_compile_error: + handle_compile_error(); + break; + case sitecing::__magic_preprocess_error: + handle_preprocess_error(); + break; + case sitecing::__magic_generic_exception: + handle_generic_exception(); + break; + default: + handle_unknown_error(); + break; + } + </%code> + <div class="powered">Powered by <a href="http://kin.klever.net/sitecing/" title="site-C-ing">site-C-ing</a>.</div> + </body> +</html> +<%method void handle_generic_exception() %> + <div class="exception-generic"> + <h1>exception caught while running component '<code><% component %></code>'</h1> + <dl class="exception-props"> + <dt><code>typeid(<em>e</em>).name()</code></dt> +% int destat; +% char *demangled = abi::__cxa_demangle(typeid(*exception_caught).name(),NULL,NULL,&destat); + <dd><code><% destat?typeid(*exception_caught).name():demangled %></code></dd> +% if(!destat) free(demangled); + <dt><code><em>e</em>.what()</code></dt> + <dd><% message %></dd> +% if(typeid(*exception_caught)==typeid(konforka::exception&)) { +% konforka::exception* ke = (konforka::exception*)exception_caught; + <dt><code><em>e</em>.where()</code></dt> + <dd><code> +% if(ke->_where.line<0) { + <% ke->where() %> +% }else{ + <% strip_roots(ke->_where.file) %>:<% ke->_where.line %> [<% ke->_where.function %>] +% } + </code></dd> +% } + </dl> +% if(typeid(*exception_caught)==typeid(konforka::exception&)) { +% konforka::exception* ke = (konforka::exception*)exception_caught; +% if(ke->_where.line>=0) { +% report_error(ke->_where.file,ke->_where.line,ke->what()); +% } +% if(!ke->_seen.empty()) { + <h2>seen at:</h2> + <div class="backtrace"> +% for(list<konforka::code_point>::const_iterator i=ke->_seen.begin();i!=ke->_seen.end();++i) { +% if(i->line>=0) { +% report_error(i->file,i->line,i->function); +% } +% } + </div> +% } +% } + </div> +</%method> +<%method void handle_preprocess_error() %> + <div class="exception-preprocess"> + <h1>error preprocessing component '<code><% component %></code>'</h1> +% report_error(root_source+component,line_number,message); + </div> +</%method> +<%method void handle_compile_error() %> + <div class="exception-compile"> + <h1>error compiling component '<code><% component %></code>'</h1> + <%code> + ifstream err((root_intermediate+component+".stderr").c_str(),ios::in); + if(err.bad()) { + <%output> + Failed to access compiler output + </%output> + }else{ + string cumulative; + string error_file; + long error_line = -1; + while(!err.eof()) { + string oef = error_file; + long oel = error_line; + string line; + getline(err,line); + if(line[0]!=' ') { + string::size_type c = line.find(':'); + if(c!=string::npos) { + string fn = line.substr(0,c); + string::size_type c1 = line.find(':',c+1); + if(c1!=string::npos) { + string ln = line.substr(c+1,c1-c-1); + string::size_type nd = ln.find_first_not_of("0123456789"); + if(nd==string::npos) { + try { + error_file = sitecing::strip_prefix(fn,"In file included from "); + }catch(sitecing::utility_no_prefix& unp) { + error_file = fn; + } + error_line = strtol(ln.c_str(),0,10); + } + } + } + if((oel>0 && !oef.empty()) && (oel!=error_line || oef!=error_file)) { + string ef = "/"+sitecing::combine_path(root_source+component,oef); + report_error(ef,oel,remove_roots(cumulative)); + cumulative.clear(); + } + } + if(!cumulative.empty()) + cumulative += '\n'; + cumulative += line; + } + if(!(cumulative.empty() || error_file.empty() || error_line<0)) { + error_file = "/"+sitecing::combine_path(root_source+component,error_file); + report_error(error_file,error_line,remove_roots(cumulative)); + } + } + </%code> + </div> +</%method> +<%method void handle_unknown_error() %> + <div class="exception-unknown"> + <h1>unknown error</h1> + </div> +</%method> +<%method void report_error(const string& file,long line,const string& message) %> + <div class="exception-codepoint-report"> + <h3><% sitecing::html_escape(strip_roots(file)) %></h3> + <%code> + if(line>=0) { + int firstline = line-5, lastline = line+5; + if(firstline<1) + firstline = 1; + ifstream ifs(file.c_str(),ios::in); + if(ifs.bad()) { + // TODO: + }else{ + for(int l=1;l<firstline && !ifs.eof();l++) { + ifs.ignore(65536,'\n'); + } + if(ifs.eof()) { + // TODO: no such line in file + }else{ + <%output><ul></%output> + for(int l=firstline;l<=lastline && !ifs.eof();l++) { + string str; + getline(ifs,str); + for(string::size_type t=str.find('\t');t!=string::npos;t=str.find('\t')) { + str.replace(t,1,8-(t%8),' '); + } + char tln[16]; + snprintf(tln,sizeof(tln),"%5d",l); + <%output> + <li class="<% l==line?"focused":"unfocused" %>"><span class="lineno"><% sitecing::html_escape(tln,sitecing::html_escape_nbsp) %></span> <span class="line"><% sitecing::html_escape(str,sitecing::html_escape_nbsp) %></span></li> + </%output> + } + <%output></ul></%output> + } + } + } + </%code> + <div class="what"> + <% sitecing::html_escape(message,sitecing::html_escape_br) %> + </div> + </div> +</%method> +<%codemethod string strip_roots(const string& filename) %> + string np = sitecing::normalize_path(filename); + try{ + return sitecing::strip_prefix(np,root_source); + }catch(sitecing::utility_no_prefix& e){ } + try{ + return sitecing::strip_prefix(np,root_intermediate); + }catch(sitecing::utility_no_prefix& e){ } +</%codemethod> +<%codemethod string remove_roots(const string& str) %> + string rv = str; + string::size_type rp; + string::size_type rl = root_source.length(); + while((rp=rv.find(root_source))!=string::npos) { + rv.erase(rp,rl); + } + rl = root_intermediate.length(); + while((rp=rv.find(root_intermediate))!=string::npos) { + rv.erase(rp,rl); + } + rl = root_so.length(); + while((rp=rv.find(root_so))!=string::npos) { + rv.erase(rp,rl); + } + return rv; +</%codemethod> +% /* vim:set ft=sitecing: */ diff --git a/components/exception_prod b/components/exception_prod new file mode 100644 index 0000000..9768623 --- a/dev/null +++ b/components/exception_prod @@ -0,0 +1,52 @@ +<%code> + /* vim:set ft=sitecing: */ + __SCIF->headers.clear(); /* reset all headers possibly set by the component throwing an exception. */ + __SCIF->out->seekp(0); /* rollback the output that the exceptional component may have produced. */ + /* set out headers */ + __SCIF->headers["Content-Type"] = "text/html"; + __SCIF->headers["Status"] = "500 server-side exception"; + __SCIF->headers["Pragma"] = "no-cache"; +</%code> +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en"> + <head> + <title>Server-side exception</title> + <style type="text/css"> + <!-- + body { + font-family: sans-serif; + font-size: 12pt; + } + h1 { + font-family: serif; + font-size: 130%; + font-weight: bold; + text-align: center; + } + p { + text-indent: 2em; + text-align: justify; + } + + div.powered { + margin: 2em 0px 0px 50%; + padding: 1ex 2ex; + text-align: right; + font-family: serif; + font-size: 140%; + font-weight: bold; + border-top: solid 2px black; + border-left: solid 1px gray; border-right: solid 1px gray; border-bottom: solid 1px gray; + background: #c0c0f0; + } + --> + </style> + </head> + <body> + <h1>server-side exception</h1> + <p>Something has gone really wrong with the server. Feel free to report the + incident to <a href="mailto:<% __CGI->get_meta("SERVER_ADMIN") %>" title="e-mail + server administrator">webmaster</a>.</p> + <div class="powered">Powered by <a href="http://kin.klever.net/sitecing/" title="site-C-ing">site-C-ing</a>.</div> + </body> +</html> diff --git a/configure.ac b/configure.ac new file mode 100644 index 0000000..acd34b9 --- a/dev/null +++ b/configure.ac @@ -0,0 +1,69 @@ +AC_INIT([sitecing], [0.0], [sitecing-bugs@klever.net]) +AC_CONFIG_SRCDIR([include/sitecing/sitecing_parser.h]) +AC_CONFIG_HEADER([config.h]) +AM_INIT_AUTOMAKE([dist-bzip2]) + +AC_PROG_INSTALL +AC_PROG_AWK +AC_PROG_CXX +AC_PROG_CC +AM_PROG_LEX +AC_PROG_LIBTOOL + +AC_HEADER_STDC +AC_CHECK_HEADERS([stdlib.h unistd.h]) + +AC_C_CONST + +AC_FUNC_MALLOC +AC_FUNC_REALLOC + +AC_WITH_PKGCONFIG + +PKG_CHECK_MODULES([KINGATE],[kingate-fcgi],,[ + AC_MSG_ERROR([no kingate library found, get it at http://kin.klever.net/kingate/]) +]) +PKG_CHECK_MODULES([DOTCONF],[dotconf],,[ + AC_MSG_ERROR([no dotconf library found]) +]) + +AC_WITH_PCRE([ + AC_WITH_PCREPP(,[ + AC_MSG_ERROR([no pcre++ library found]) + ]) +],[ + AC_MSG_ERROR([no pcre library found]) +]) + +AC_CHECK_LIB([dl],[dlopen],,[ + AC_MSG_ERROR([no dlopen library found]) +]) + +AC_PATH_PROG([XSLTPROC],[xsltproc],[true]) + +WANT_DOXYGEN="yes" +AC_ARG_ENABLE([doxygen], + AC_HELP_STRING([--disable-doxygen],[do not generate documentation]), + [ + test "${enableval}" = "no" && WANT_DOXYGEN="no" + ] +) +if test "${WANT_DOXYGEN}" = "yes" ; then + AC_WITH_DOXYGEN + AC_WITH_DOT +else + AM_CONDITIONAL([HAVE_DOXYGEN],[false]) + AM_CONDITIONAL([HAVE_DOT],[false]) +fi + +AC_CONFIG_FILES([ + Makefile + Doxyfile + sitecing.pc + include/Makefile + lib/Makefile + share/Makefile + src/Makefile + components/Makefile +]) +AC_OUTPUT diff --git a/include/.gitignore b/include/.gitignore new file mode 100644 index 0000000..3dda729 --- a/dev/null +++ b/include/.gitignore @@ -0,0 +1,2 @@ +Makefile.in +Makefile diff --git a/include/Makefile.am b/include/Makefile.am new file mode 100644 index 0000000..88148d5 --- a/dev/null +++ b/include/Makefile.am @@ -0,0 +1,18 @@ +# TODO: separate installable headers from internals +# but to make it sensible one need to elimitate +# abnoxious sitecing_interface_cgi.h inclusion of +# sitespace.h +nobase_include_HEADERS = \ + sitecing/acomponent.h sitecing/cgi_component.h \ + sitecing/component_so.h \ + sitecing/file_factory.h sitecing/component_factory.h \ + sitecing/sitecing_interface.h sitecing/sitecing_interface_cgi.h \ + sitecing/sitecing_parser.h sitecing/sitecing_enflesher.h \ + sitecing/sitespace.h \ + sitecing/configuration.h \ + sitecing/magic.h sitecing/util.h \ + sitecing/exception.h sitecing/sitecing_exception.h \ + sitecing/sitecing_util.h \ + sitecing/process_manager.h \ + sitecing/scoreboard.h + diff --git a/include/sitecing/acomponent.h b/include/sitecing/acomponent.h new file mode 100644 index 0000000..160e854 --- a/dev/null +++ b/include/sitecing/acomponent.h @@ -0,0 +1,78 @@ +#ifndef __SITECING_ACOMPONENT_H +#define __SITECING_ACOMPONENT_H + +#include "sitecing/sitecing_interface.h" + +/** + * @file + * @brief The acomponent class declaration. + */ + +namespace sitecing { + + /** + * An abstract base class for sitecing components. + */ + class acomponent { + public: + /** + * Pointer to the interface object, used to communicate with the + * site-C-ing core. + */ + sitecing_interface *__SCIF; + + acomponent(); + virtual ~acomponent(); + + /** + * Set the interface to core pointer. + * @param scif the pointer to the interface object. + */ + virtual void __set_interface(sitecing_interface *scif=0); + /** + * Invoked if the interface to the core has changed. + * @param oscif pointer to the old interface object. + */ + virtual void __on_change_interface(sitecing_interface *oscif); + + /** + * do import components. + */ + virtual void __do_imports(); + /** + * invoked on components imports. + */ + virtual void __on_imports(); + /** + * fetch the pointer to the most derived component. + * @returns pointer to the most derived object. + */ + virtual void *__the_most_derived_this() = 0; + + /** + * Do the job. + * @param __magic the magic number used as a key to decipher the + * rest of parameters. + * @param __args the parameters. + */ + virtual void main(int __magic,va_list __args) = 0; + + /** + * Run the component. Convenience helper for calling main(). + * @param __magic the magic number. + * @param ... the rest of parameters. + * @see main(); + */ + void run(int __magic,...); + + /** + * Helper function (which doesn't necessarily belongs here!) for + * reading the file and passing it to the output stream. + * @param fn the file name. + */ + void pass_file_through(const char *fn); + }; + +} + +#endif /* __SITECING_ACOMPONENT_H */ diff --git a/include/sitecing/cgi_component.h b/include/sitecing/cgi_component.h new file mode 100644 index 0000000..91df689 --- a/dev/null +++ b/include/sitecing/cgi_component.h @@ -0,0 +1,54 @@ +#ifndef __SITECING_CGI_COMPONENT_H +#define __SITECING_CGI_COMPONENT_H + +#include <map> +#include "kingate/cgi_gateway.h" +#include "sitecing/acomponent.h" +#include "sitecing/sitecing_interface_cgi.h" + +/** + * @file + * @brief The cgi_component class declaration. + */ + +namespace sitecing { + using namespace std; + + /** + * The CGI-oriented component class. + */ + class cgi_component : virtual public acomponent { + public: + /** + * The interface to site-C-ing core. + */ + sitecing_interface_cgi* __SCIF; + /** + * The interface to the CGI gateway. + */ + kingate::cgi_gateway* __CGI; + + cgi_component(); + virtual ~cgi_component(); + + /** + * @overload acomponent::__set_interface() + */ + void __set_interface(sitecing_interface* scif); + /** + * @overload acomponent::__on_change_interface() + */ + void __on_change_interface(sitecing_interface *o); + /** + * Invoked on the change of the interface to the CGI. + */ + virtual void __on_change_CGI(kingate::cgi_gateway *o); + /** + * @overload acomponent::__on_imports() + */ + virtual void __on_imports(); + }; + +} + +#endif /* __SITECING_CGI_COMPONENT_H */ diff --git a/include/sitecing/component_factory.h b/include/sitecing/component_factory.h new file mode 100644 index 0000000..a208ed1 --- a/dev/null +++ b/include/sitecing/component_factory.h @@ -0,0 +1,84 @@ +#ifndef __SITECING_COMPONENT_FACTORY_H +#define __SITECING_COMPONENT_FACTORY_H + +#include <string> +#include <list> +#include <stdexcept> +#include "sitecing/file_factory.h" +#include "sitecing/configuration.h" + +/** + * @file + * @brief The component_factory class declaration. + */ + +namespace sitecing { + using namespace std; + + /** + * @brief The components builder. + */ + class component_factory : public file_factory { + public: + /** + * Path to the source files root. + */ + string root_source; + /** + * Path to the root of the intermediate files storage. + */ + string root_intermediate; + /** + * Output path for .so components. + */ + string root_so; + /** + * Reference to the configuration container. + */ + configuration& config; + + /** + * @param c reference to the configuration container. + */ + component_factory(configuration& c); + + /** + * @overload file_factory::get_dependencies() + */ + virtual void get_dependencies(const string& dst,file_list_t& deps); + /** + * @overload file_factory::is_uptodate() + */ + virtual bool is_uptodate(const string& dst,file_list_t *deps=NULL); + /** + * @overload file_factory::build() + */ + virtual void build(const string& dst); + + /** + * Helper function for executing external command. + * @param cmd the command to execute. + * @param args the command line arguments. + * @param stdo stdout for the child process. + * @param stde stderr for the child process. + * @return exit code. + */ + int execute(const string& cmd,const list<string>& args,int stdo,int stde); + /** + * Fetch the class name of the component. + * @param component the component. + * @return the class name. + */ + string get_classname(const string& component); + /** + * Get the components from which the target component has been + * derived. + * @param component the target component + * @param rv where to store the list of ancestors. + */ + void get_ancestors(const string& component,file_list_t &rv); + }; + +} + +#endif /* __SITECING_COMPONENT_FACTORY_H */ diff --git a/include/sitecing/component_so.h b/include/sitecing/component_so.h new file mode 100644 index 0000000..3239d4a --- a/dev/null +++ b/include/sitecing/component_so.h @@ -0,0 +1,159 @@ +#ifndef __SITECING_COMPONENT_SO_H +#define __SITECING_COMPONENT_SO_H + +#include <sys/types.h> +#include <sys/stat.h> +#include <string> +#include <map> +#include <list> +#include "sitecing/acomponent.h" + +/** + * @file + * @brief Classes related to the .so components handling. + */ + +namespace sitecing { + using namespace std; + + /** + * The 'class' component object. + */ + class component_so { + public: + /** + * The type of the component instantiating function. + */ + typedef acomponent *(*egg_t)(); + /** + * Type for storing the list of instances and the reference counts. + */ + typedef map<acomponent*,int> used_chickens_t; + /** + * The type for storing the list of unused instances. + */ + typedef list<acomponent*> free_chickens_t; + + /** + * The .so file name. + */ + string sofile; + /** + * The stat structure for the .so loaded. + */ + struct stat stso; + /** + * The dloaded .so handle. + */ + void *dl; + /** + * Pointer to the instatiator function. + */ + egg_t egg; + /** + * The list of instances in use. + */ + used_chickens_t chickens_used; + /** + * The list of unused instances. + */ + free_chickens_t chickens_free; + + /** + * @param soname the .so file name + */ + component_so(const string& soname); + ~component_so(); + /** + * Check whether the loaded .so is in sync with the disk file. + */ + bool is_uptodate() const; + + /** + * @todo TODO: wish I could remember -- document me. + */ + acomponent* allocate_chicken(); + /** + * @todo TODO: wish I could remember -- document me. + */ + void allocate_chicken(acomponent *ac); + /** + * @todo TODO: wish I could remember -- document me. + */ + void deallocate_chicken(acomponent *ac); + }; + + /** + * The component instance container. + */ + class so_component { + public: + /** + * Pointer to the component 'class'. + */ + component_so *hen; + /** + * The instance in question. + */ + acomponent* ac; + + so_component() + : hen(0), ac(0) { } + /** + * @param h the 'class' object. + * @param scif pointer to the interface to the site-C-ing core. + */ + so_component(component_so *h,sitecing_interface *scif); + /** + * Copy constructor + * @param s source instance. + */ + so_component(const so_component& s) + : hen(0), ac(0) { attach(s); } + ~so_component() { detach(); } + + /** + * Assignment operator. + * @param s source instance. + */ + so_component& operator=(const so_component& s) { + attach(s); return *this; + } + + /** + * @todo TODO: wish I could remember the details -- document me. + * @param h the 'class' object. + * @param a the instance to be attached. + */ + void attach(component_so *h,acomponent *a); + /** + * @todo TODO: wish I could remember the details -- document me. + * @param s the source instance. + */ + void attach(const so_component& s) { attach(s.hen,s.ac); } + /** + * @todo TODO: wish I could remember the details -- document me. + */ + void detach(); + }; + + /** + * The typed component instance container template. + * @param CT the component class. + */ + template<typename CT> + class so_component_t : public sitecing::so_component { + public: + /** + * @param s The untyped instance container. + */ + so_component_t(const so_component& s) + : so_component(s) { } + CT* operator->() { + return static_cast<CT*>(ac->__the_most_derived_this()); + } + }; + +} + +#endif /* __SITECING_COMPONENT_SO_H */ diff --git a/include/sitecing/configuration.h b/include/sitecing/configuration.h new file mode 100644 index 0000000..330a5a6 --- a/dev/null +++ b/include/sitecing/configuration.h @@ -0,0 +1,459 @@ +#ifndef __SITECING_CONFIGURATION_H +#define __SITECING_CONFIGURATION_H + +#include <sys/types.h> +#include <sys/stat.h> +#include <string> +#include <list> +#include <map> +#include <pcre++.h> + +/** + * @file + * @brief Classes related to configuration. + */ + +namespace sitecing { + using namespace std; + + class configuration; + + /** + * The config options container class. + */ + class config_options { + public: + /** + * The flags enumeration. + */ + enum _flags { + /** + * Skeleton has been specified in the context. + * @see skeleton + */ + flag_skeleton = 0x0001, + /** + * CPPFLAGS have been specified in the context + * @see cpp_flags + */ + flag_cpp_flags = 0x0002, + /** + * LDFLAGS have been specified in the context. + * @see ld_flags + */ + flag_ld_flags = 0x0004, + /** + * Enforced intermediate dependencies have been specified in the + * context. + * @see internediate_deps + */ + flag_intermediate_deps = 0x0008, + /** + * Enforced .so dependencies have been specified in the context. + * @see so_deps + */ + flag_so_deps = 0x0010, + /** + * Whether components should be built specified in the context. + * @see build + */ + flag_build = 0x0020, + /** + * Whether to track down cpp dependencies has been specified in + * the context. + * @see cpp_deps + */ + flag_cpp_deps = 0x0040, + /** + * The exception handler has been specified in the context. + * @see exception_handler + */ + flag_exception_handler = 0x0080, + /** + * Action handlers have been specified in the context. + * @see action_handlers + */ + flag_action_handlers = 0x0100, + /** + * HTTP status handerls have been specified in the context. + * @see http_status_handlers + */ + flag_http_status_handlers = 0x0200, + /** + * The files to be built are specified in the context. + * @see auto_build_files + */ + flag_auto_build_files = 0x0400 + }; + /** + * The flags specifying what parts of configuration have been + * specified for the context. + */ + int flags; + + /** + * The skeleton for building components. + */ + string skeleton; + /** + * The flags to pass to compiler. + */ + list<string> cpp_flags; + /** + * The flags to pass to linker. + */ + list<string> ld_flags; + /** + * Whether to build inexstent and outdated components. + */ + bool build; + /** + * Whether to track cpp dependencies. + */ + bool cpp_deps; + /** + * The component handling caught exceptions. + */ + string exception_handler; + /** + * Enforced intermediate dependencies. + */ + list<string> intermediate_deps; + /** + * Enforced depencies for .so objects. + */ + list<string> so_deps; + /** + * The action handler type. + */ + struct action_handler_t { + /** + * The regexp to check request against. + */ + string s_regex; + /** + * Precompiled regex. + */ + pcrepp::Pcre regex; + /** + * The action handler component. + */ + string action; + /** + * Arguments for the action hander coponent. + */ + list<string> args; + + action_handler_t(const string& s,const string& a) + : s_regex(s), regex(s), action(a) { } + action_handler_t(const action_handler_t& s) + : s_regex(s.s_regex), regex(s.regex), action (s.action), args(s.args) { } + }; + /** + * Type for the list of action handlers. + */ + typedef list<action_handler_t> action_handlers_t; + /** + * The list of action handlers. + */ + action_handlers_t action_handlers; + /** + * Type for the map of HTTP status handler components. + */ + typedef map<string,string> http_status_handlers_t; + /** + * The map of HTTP status handler components. + */ + http_status_handlers_t http_status_handlers; + /** + * Files to be built automatically. + */ + list<string> auto_build_files; + + config_options() + : flags(0) { } + + /** + * Look up if there is an action handler for the target defined. + * @param target the target component. + * @return the pointer to handler or zero. + */ + action_handler_t *lookup_action_handler(const string& target); + /** + * Look up if there is a handler defined for the HTTP status. + * @param status HTTP status + * @return the handler component. + */ + string lookup_http_status_handler(const string& status); + /** + * Check whether the file should be build automatically. + * @param fn file name. + * @param rv reference to the boolean where the answer should go. + * @return true if we know the answer. + */ + bool match_autobuild_files(const char *fn,bool &rv); + }; + + /** + * Configuration data container for the configuration loaded from disk file. + */ + class loaded_options : public config_options { + public: + /** + * The file where configuration originates from. + */ + string source_file; + /** + * The stat structure for the source file as it was when we have + * loaded it. + */ + struct stat st; + + /** + * See if the data is still valid. + * @return true if yes. + */ + bool is_valid(); + + /** + * Load the configuration file. + * @param config the main configuration container. + * @param the configuration file. + */ + void parse(configuration *config,const string& cfile); + }; + + /** + * The main configuration container. + */ + class configuration { + public: + /** + * @todo TODO:: document me. + */ + bool autobuild; + /** + * The flags enumeration. + */ + enum _flags { + /** + * Was the source root specified? + * @see root_source + */ + flag_root_source = 0x00000001, + /** + * Was the root for intermediate files specified? + * @see root_intermediate + */ + flag_root_intermediate = 0x00000002, + /** + * Was the root for the resulting .so files specified? + * @see root_so + */ + flag_root_so = 0x00000004, + /** + * Was the socket to listen to specified? + * @see listen_socket + */ + flag_listen_socket = 0x00000008, + /** + * Was the per-dir config file name specified. + * @see rc_file_name + */ + flag_rc_file_name = 0x00000010, + /** + * Was the minimum number of child processes specified? + * @see min_children + */ + flag_min_children = 0x00000020, + /** + * Was the maximum number of child processes specified? + * @see max_children + */ + flag_max_children = 0x00000040, + /** + * Was the minimum number of spare child processes specified? + * @see min_spare_children + */ + flag_min_spare_children = 0x00000080, + /** + * Was he maximum number of spare child processes specified? + * @see max_spare_children + */ + flag_max_spare_children = 0x00000100, + /** + * Was the number of requests to handle per child process + * specified? + * @see requests_per_child + */ + flag_requests_per_child = 0x00000200, + /** + * Was the multiprocess node (or it's absences) specified? + * @see multi_process + */ + flag_multi_process = 0x00000400, + /** + * Was the user specified? + * @see user + */ + flag_user = 0x00000800, + /** + * @Was the group specified? + * @see group + */ + flag_group = 0x00001000, + /** + * Was the root to change to specified? + * @see chroot + */ + flag_chroot = 0x00002000, + /** + * Was the file for storing PID specified? + * @see pidfile + */ + flag_pid_file = 0x00004000, + /** + * Was it specified wether we should daemonize the process? + * @see daemonize + */ + flag_daemonize = 0x00008000 + }; + /** + * The flags specifying what parts of the configuration has been + * loaded into the object. + */ + long flags; + + /** + * The root for the components source code. + */ + string root_source; + /** + * The root for intermediate files. + */ + string root_intermediate; + /** + * The root for .so files. + */ + string root_so; + /** + * Socket to bind to + */ + string listen_socket; + /** + * per-dir config file name. + */ + string rc_file_name; + /** + * The minimum number of child processes in multiprocess mode. + */ + int min_children; + /** + * The maxium number of child processes in multiprocess mode. + */ + int max_children; + /** + * The minimum number of spare chidren in multiprocess mode. + */ + int min_spare_children; + /** + * The maximum number of spare children in multiprocess mode. + */ + int max_spare_children; + /** + * The number of requests the child process should handle before + * exiting. + */ + int requests_per_child; + /** + * Whether we should run in multiprocess mode or not. + */ + bool multi_process; + /** + * User to change to. + */ + string user; + /** + * Group to set to. + */ + string group; + /** + * Directory to change root to. + */ + string chroot; + /** + * The file to store PID into. + */ + string pid_file; + /** + * Whether we should fork into background. + */ + bool daemonize; + + typedef map<string,config_options> specs_t; + /** + * The local config options map. + */ + specs_t specs; + typedef map<string,loaded_options> loaded_specs_t; + /** + * The local config options as specified in per-dir config files + * map. + */ + loaded_specs_t loaded_specs; + + configuration(); + /** + * @param cfile the configuration file. + * @param ab @todo TODO:: document me + */ + configuration(const string& cfile,bool ab=false); + + /** + * Parse the configuration file. + * @param cfile the configuration file. + */ + void parse(const string& cfile); + + /** + * Fetch the reference to options for the very root. + */ + config_options& root_options() { return specs[""]; } + /** + * Lookup where the certain config option for the target lies in. + * @param target the target component. + * @param flag the flag specifying the option we're looking for. + * @return the destination options continer or zero. + */ + config_options* lookup_config(const string& target,int flag); + /** + * Lookup the action handler for the target. + * @param target the target request. + * @return the action handler or zero. + */ + config_options::action_handler_t *lookup_action_handler(const string& target); + /** + * Lookup the HTPP status handler for the target. + * @param target the target. + * @param status the HTTP status. + * @return the handler component. + */ + string lookup_http_status_handler(const string& target,const string& status); + /** + * Lookup the options loaded from per-dir config for the target + * @param target the target. + * @return options container or zero. + */ + loaded_options* lookup_loaded_options(const string& target); + /** + * Check whether the components for the target should be prebuilt. + * @param target the target. + * @param fn file name. + * @return true if yes. + */ + bool match_autobuild_files(const string& target,const char *fn); + }; + +} + +#endif /* __SITECING_CONFIGURATION_H */ diff --git a/include/sitecing/exception.h b/include/sitecing/exception.h new file mode 100644 index 0000000..985fc6d --- a/dev/null +++ b/include/sitecing/exception.h @@ -0,0 +1,542 @@ +#ifndef __SITECING_EXCEPTION_H +#define __SITECING_EXCEPTION_H + +/** + * @file + * @brief The site-C-ing specific exceptions. + */ + +/** + * @brief The main site-C-ing namespace. + */ +namespace sitecing { + + // TODO: status specifics + + /** + * The http status to return. + */ + class http_status { + public: + /** + * The status string. + */ + string status; + /** + * The message to follow the status string. + */ + string message; + + /** + * @param s HTTP status. + * @param m HTTP status message. + */ + http_status(const string& s,const string& m) + : status(s), message(m) { } + virtual ~http_status() throw() { } + }; + + // per RFC 2616 + + /** + * Informational. + */ + class http_status_1xx : public http_status { + public: + explicit http_status_1xx(const string &s,const string& m) + : http_status(s,m) { } + }; + /** + * Continue. + */ + class http_status_100 : public http_status_1xx { + public: + explicit http_status_100(const string& m) + : http_status_1xx("100",m) { } + explicit http_status_100() + : http_status_1xx("100","Continue") { } + }; + /** + * Switching protocols. + */ + class http_status_101 : public http_status_1xx { + public: + explicit http_status_101(const string& m) + : http_status_1xx("101",m) { } + explicit http_status_101() + : http_status_1xx("101","Switching protocols") { } + }; + + /** + * Successful. + */ + class http_status_2xx : public http_status { + public: + explicit http_status_2xx(const string& s,const string& m) + : http_status(s,m) { } + }; + /** + * OK. + */ + class http_status_200 : public http_status_2xx { + public: + explicit http_status_200(const string& m) + : http_status_2xx("200",m) { } + explicit http_status_200() + : http_status_2xx("200","OK") { } + }; + /** + * Created. + */ + class http_status_201 : public http_status_2xx { + public: + explicit http_status_201(const string& m) + : http_status_2xx("201",m) { } + explicit http_status_201() + : http_status_2xx("201","Created") { } + }; + /** + * Accepted. + */ + class http_status_202 : public http_status_2xx { + public: + explicit http_status_202(const string& m) + : http_status_2xx("202",m) { } + explicit http_status_202() + : http_status_2xx("202","Accepted") { } + }; + /** + * Non-authoritative infortmation. + */ + class http_status_203 : public http_status_2xx { + public: + explicit http_status_203(const string& m) + : http_status_2xx("203",m) { } + explicit http_status_203() + : http_status_2xx("203","Non-authoritative information") { } + }; + /** + * No content. + */ + class http_status_204 : public http_status_2xx { + public: + explicit http_status_204(const string& m) + : http_status_2xx("204",m) { } + explicit http_status_204() + : http_status_2xx("204","No content") { } + }; + /** + * Reset content. + */ + class http_status_205 : public http_status_2xx { + public: + explicit http_status_205(const string& m) + : http_status_2xx("205",m) { } + explicit http_status_205() + : http_status_2xx("205","Reset content") { } + }; + /** + * Partial content. + */ + class http_status_206 : public http_status_2xx { + public: + explicit http_status_206(const string& m) + : http_status_2xx("206",m) { } + explicit http_status_206() + : http_status_2xx("206","Partial content") { } + }; + + /** + * Redirection. + */ + class http_status_3xx : public http_status { + public: + explicit http_status_3xx(const string& s,const string& m) + : http_status(s,m) { } + }; + /** + * Multiple choices. + */ + class http_status_300 : public http_status_3xx { + public: + explicit http_status_300(const string& m) + : http_status_3xx("300",m) { } + explicit http_status_300() + : http_status_3xx("300","Multiple choices") { } + }; + /** + * Moved permanently. + */ + class http_status_301 : public http_status_3xx { + public: + explicit http_status_301(const string& m) + : http_status_3xx("301",m) { } + explicit http_status_301() + : http_status_3xx("301","Moved permanently") { } + }; + /** + * Found. + */ + class http_status_302 : public http_status_3xx { + public: + explicit http_status_302(const string& m) + : http_status_3xx("302",m) { } + explicit http_status_302() + : http_status_3xx("302","Found") { } + }; + /** + * See other. + */ + class http_status_303 : public http_status_3xx { + public: + explicit http_status_303(const string& m) + : http_status_3xx("303",m) { } + explicit http_status_303() + : http_status_3xx("303","See other") { } + }; + /** + * Not modified. + */ + class http_status_304 : public http_status_3xx { + public: + explicit http_status_304(const string& m) + : http_status_3xx("304",m) { } + explicit http_status_304() + : http_status_3xx("304","Not modified") { } + }; + /** + * Use proxy. + */ + class http_status_305 : public http_status_3xx { + public: + explicit http_status_305(const string& m) + : http_status_3xx("305",m) { } + explicit http_status_305() + : http_status_3xx("305","Use proxy") { } + }; + // 306 is unused and reserved + /** + * Temporary redirect. + */ + class http_status_307 : public http_status_3xx { + public: + explicit http_status_307(const string& m) + : http_status_3xx("307",m) { } + explicit http_status_307() + : http_status_3xx("307","Temporary redirect") { } + }; + + /** + * Error. + */ + class http_status_4xx : public http_status { + public: + explicit http_status_4xx(const string& s,const string& m) + : http_status(s,m) { } + }; + /** + * Bad request. + */ + class http_status_400 : public http_status_4xx { + public: + explicit http_status_400(const string& m) + : http_status_4xx("400",m) { } + explicit http_status_400() + : http_status_4xx("400","Bad request") { } + }; + /** + * Unauthorized. + */ + class http_status_401 : public http_status_4xx { + public: + explicit http_status_401(const string& m) + : http_status_4xx("401",m) { } + explicit http_status_401() + : http_status_4xx("401","Unauthorized") { } + }; + /** + * Payment required. + */ + class http_status_402 : public http_status_4xx { + public: + explicit http_status_402(const string& m) + : http_status_4xx("402",m) { } + explicit http_status_402() + : http_status_4xx("402","Payment required") { } + }; + /** + * Forbidden. + */ + class http_status_403 : public http_status_4xx { + public: + explicit http_status_403(const string& m) + : http_status_4xx("403",m) { } + explicit http_status_403() + : http_status_4xx("403","Forbidden") { } + }; + /** + * Not found. + */ + class http_status_404 : public http_status_4xx { + public: + explicit http_status_404(const string& m) + : http_status_4xx("404",m) { } + explicit http_status_404() + : http_status_4xx("404","Not found") { } + }; + /** + * Method not allowed. + */ + class http_status_405 : public http_status_4xx { + public: + explicit http_status_405(const string& m) + : http_status_4xx("405",m) { } + explicit http_status_405() + : http_status_4xx("405","Method not allowed") { } + }; + /** + * Not acceptable. + */ + class http_status_406 : public http_status_4xx { + public: + explicit http_status_406(const string& m) + : http_status_4xx("406",m) { } + explicit http_status_406() + : http_status_4xx("406","Not acceptable") { } + }; + /** + * Proxy authentication required. + */ + class http_status_407 : public http_status_4xx { + public: + explicit http_status_407(const string& m) + : http_status_4xx("407",m) { } + explicit http_status_407() + : http_status_4xx("407","Proxy authentication required") { } + }; + /** + * Request timeout. + */ + class http_status_408 : public http_status_4xx { + public: + explicit http_status_408(const string& m) + : http_status_4xx("408",m) { } + explicit http_status_408() + : http_status_4xx("408","Request timeout") { } + }; + /** + * Conflict. + */ + class http_status_409 : public http_status_4xx { + public: + explicit http_status_409(const string& m) + : http_status_4xx("409",m) { } + explicit http_status_409() + : http_status_4xx("409","Conflict") { } + }; + /** + * Gone. + */ + class http_status_410 : public http_status_4xx { + public: + explicit http_status_410(const string& m) + : http_status_4xx("410",m) { } + explicit http_status_410() + : http_status_4xx("410","Gone") { } + }; + /** + * Length required. + */ + class http_status_411 : public http_status_4xx { + public: + explicit http_status_411(const string& m) + : http_status_4xx("411",m) { } + explicit http_status_411() + : http_status_4xx("411","Length required") { } + }; + /** + * Precondition failed. + */ + class http_status_412 : public http_status_4xx { + public: + explicit http_status_412(const string& m) + : http_status_4xx("412",m) { } + explicit http_status_412() + : http_status_4xx("412","Precondition failed") { } + }; + /** + * Request entity too large. + */ + class http_status_413 : public http_status_4xx { + public: + explicit http_status_413(const string& m) + : http_status_4xx("413",m) { } + explicit http_status_413() + : http_status_4xx("413","Request entity too large") { } + }; + /** + * Request URI too long. + */ + class http_status_414 : public http_status_4xx { + public: + explicit http_status_414(const string& m) + : http_status_4xx("414",m) { } + explicit http_status_414() + : http_status_4xx("414","Request URI too long") { } + }; + /** + * Unsupported media type. + */ + class http_status_415 : public http_status_4xx { + public: + explicit http_status_415(const string& m) + : http_status_4xx("415",m) { } + explicit http_status_415() + : http_status_4xx("415","Unsupported media type") { } + }; + /** + * Requested range not satisfiable. + */ + class http_status_416 : public http_status_4xx { + public: + explicit http_status_416(const string& m) + : http_status_4xx("416",m) { } + explicit http_status_416() + : http_status_4xx("416","Requested range not satisfiable") { } + }; + /** + * Expectation failed. + */ + class http_status_417 : public http_status_4xx { + public: + explicit http_status_417(const string& m) + : http_status_4xx("417",m) { } + explicit http_status_417() + : http_status_4xx("417","Expectation failed") { } + }; + + /** + * Server error. + */ + class http_status_5xx : public http_status { + public: + explicit http_status_5xx(const string& s,const string& m) + : http_status(s,m) { } + }; + /** + * Internal server error. + */ + class http_status_500 : public http_status_5xx { + public: + explicit http_status_500(const string& m) + : http_status_5xx("500",m) { } + explicit http_status_500() + : http_status_5xx("500","Internal server error") { } + }; + /** + * Not implemented. + */ + class http_status_501 : public http_status_5xx { + public: + explicit http_status_501(const string& m) + : http_status_5xx("501",m) { } + explicit http_status_501() + : http_status_5xx("501","Not implemented") { } + }; + /** + * Bad gateway. + */ + class http_status_502 : public http_status_5xx { + public: + explicit http_status_502(const string& m) + : http_status_5xx("502",m) { } + explicit http_status_502() + : http_status_5xx("502","Bad gateway") { } + }; + /** + * Service unavailable. + */ + class http_status_503 : public http_status_5xx { + public: + explicit http_status_503(const string& m) + : http_status_5xx("503",m) { } + explicit http_status_503() + : http_status_5xx("503","Service unavailable") { } + }; + /** + * Gateway timeout. + */ + class http_status_504 : public http_status_5xx { + public: + explicit http_status_504(const string& m) + : http_status_5xx("504",m) { } + explicit http_status_504() + : http_status_5xx("504","Gateway timeout") { } + }; + /** + * HTTP version not supported. + */ + class http_status_505 : public http_status_5xx { + public: + explicit http_status_505(const string& m) + : http_status_5xx("505",m) { } + explicit http_status_505() + : http_status_5xx("505","HTTP version not supported") { } + }; + + // Aliases + + typedef http_status_1xx http_status_informational; + typedef http_status_100 http_status_continue; + typedef http_status_101 http_status_switching_protocols; + + typedef http_status_2xx http_status_sucessful; + typedef http_status_200 http_status_ok; + typedef http_status_201 http_status_created; + typedef http_status_202 http_status_accepted; + typedef http_status_203 http_status_non_authoritative_information; + typedef http_status_204 http_status_no_content; + typedef http_status_205 http_status_reset_content; + typedef http_status_206 http_status_partial_content; + + typedef http_status_3xx http_status_redirection; + typedef http_status_300 http_status_multiple_choices; + typedef http_status_301 http_status_moved_permanently; + typedef http_status_302 http_status_found; + typedef http_status_303 http_status_see_other; + typedef http_status_304 http_status_not_modified; + typedef http_status_305 http_status_use_proxy; + // 306 is unused and reserved + typedef http_status_307 http_status_temporary_redirect; + + typedef http_status_4xx http_status_client_error; + typedef http_status_400 http_status_bad_request; + typedef http_status_401 http_status_unauthorized; + typedef http_status_402 http_status_payment_required; + typedef http_status_403 http_status_forbidden; + typedef http_status_404 http_status_not_found; + typedef http_status_405 http_status_method_not_allowed; + typedef http_status_406 http_status_not_acceptable; + typedef http_status_407 http_status_proxy_authentication_required; + typedef http_status_408 http_status_request_timeout; + typedef http_status_409 http_status_conflict; + typedef http_status_410 http_status_gone; + typedef http_status_411 http_status_length_required; + typedef http_status_412 http_status_precondition_failed; + typedef http_status_413 http_status_request_entity_too_large; + typedef http_status_414 http_status_requrest_uri_too_long; + typedef http_status_415 http_status_unsupported_media_type; + typedef http_status_416 http_status_required_range_not_satisfiable; + typedef http_status_417 http_status_expectation_failed; + + typedef http_status_5xx http_status_server_error; + typedef http_status_500 http_status_internal_server_error; + typedef http_status_501 http_status_not_implemented; + typedef http_status_502 http_status_bad_gateway; + typedef http_status_503 http_status_service_unavailable; + typedef http_status_504 http_status_gateway_timeout; + typedef http_status_505 http_status_http_version_not_supported; + +} + +#endif /* __SITECING_EXCEPTION_H */ diff --git a/include/sitecing/file_factory.h b/include/sitecing/file_factory.h new file mode 100644 index 0000000..7ec82da --- a/dev/null +++ b/include/sitecing/file_factory.h @@ -0,0 +1,64 @@ +#ifndef __SITECING_FILE_FACTORY_H +#define __SITECING_FILE_FACTORY_H + +#include <string> +#include <list> + +/** + * @file + * @brief the file_factory class declaration. + */ + +namespace sitecing { + using namespace std; + + /** + * The factory class. Does the job similar to that which is done by make + * utility. + */ + class file_factory { + public: + /** + * The recursion depth. + */ + int depth; + /** + * The list of files type. The list of strings, in fact. + */ + typedef list<string> file_list_t; + + file_factory() + : depth(0) { } + + /** + * Fetch depndencies for the given file. + * @param dst destination file. + * @param deps where to put dependencies to. + */ + virtual void get_dependencies(const string& dst,file_list_t& deps) = 0; + /** + * Check if the destination is up to day. + * @param the destination file. + * @param deps if the deps pointer is non there, the dependencies + * retrieved will be stored there. + * @return true if yes. + * @see get_dependencies() + */ + virtual bool is_uptodate(const string& dst,file_list_t* deps=0); + /** + * Build the file requested. + * @param dst the file requested. + */ + virtual void build(const string& dst) = 0; + /** + * Make the file requested, which means: build it, unless it's + * uptodate. + * @see is_uptodate() + * @see build() + */ + virtual void make(const string& dst); + }; + +} + +#endif /* __SITECING_FILE_FACTORY_H */ diff --git a/include/sitecing/magic.h b/include/sitecing/magic.h new file mode 100644 index 0000000..4802fcc --- a/dev/null +++ b/include/sitecing/magic.h @@ -0,0 +1,63 @@ +#ifndef __SITECING_MAGIC_H +#define __SITECING_MAGIC_H + +/** + * @file + * @brief The magic numbers globally defined. + */ + +namespace sitecing { + + /** + * The magic numbers enumeration. + */ + enum { + /** + * There is no magic. + */ + __magic_none = 0, + /** + * Here is where user-defined magic starts. + */ + __user_magical_numbers_start = 1, + /** + * Here is where site-C-ing defined magic starts. + */ + __sitecing_magical_numbers_start = 0x8000, + /** + * The compiler error occured. The parameters passed are: + * + * char *message, char *root_source, char *root_intermediate, char *root_so, char *component + */ + __magic_compile_error, + /** + * The preprocessor error occured. The parameters passed are: + * + * char *message, char *root_source, char *root_intermediate, char *root_so, char *component, + * int line_number + */ + __magic_preprocess_error, + /** + * Exception caught while executing the component. The parameters passed are: + * + * char *message, char *root_source, char *root_intermediate, char *root_so, char *component, + * const exception *exception_caught + */ + __magic_generic_exception, + /** + * The component called as an action handler. The parameters passed are: + * + * char *root_source, char *root_intermediate, char *root_so, list<string>* args + */ + __magic_action, + /** + * The component called as an HTTP status handler. The parameters passed are: + * + * char *root_source, char *root_intermediate, char *root_so, char *component, + * const http_status *http_status_caught + */ + __magic_http_status + }; +} + +#endif /* __SITECING_MAGIC_H */ diff --git a/include/sitecing/process_manager.h b/include/sitecing/process_manager.h new file mode 100644 index 0000000..73415d3 --- a/dev/null +++ b/include/sitecing/process_manager.h @@ -0,0 +1,89 @@ +#ifndef __SITECING_PROCESS_MANAGER_H +#define __SITECING_PROCESS_MANAGER_H + +#include <sitecing/scoreboard.h> + +/** + * @file + * @brief the process manager. + */ + +namespace sitecing { + + /** + * The process manager. + */ + class process_manager { + public: + /** + * Minimum number of child processes. + */ + int min_children; + /** + * Maxinum number of child processes. + */ + int max_children; + /** + * Minimum number of spare child processes. + */ + int min_spare_children; + /** + * Maxiumum number of spare child processes. + */ + int max_spare_children; + /** + * The scoreboard. + */ + scoreboard sboard; + /** + * We're in the process of shutting down. + */ + bool finishing; + /** + * @todo TODO: wish I could rememer -- document me. + */ + bool die_humbly; + + process_manager(); + virtual ~process_manager(); + + /** + * The main loop. + */ + void manage(); + + /** + * The worker function. + * @param the slot allocated for the process. + */ + virtual void process(int slot) = 0; + + /** + * @todo TODO: wish I could remember -- document me. + */ + void manage_children(); + /** + * @todo TODO: wish I could remember -- document me. + */ + bool spawn_children(); + /** + * @todo TODO: wish I could remember -- document me. + */ + bool kill_children(); + /** + * @todo TODO: wish I could remember -- document me. + */ + void spawn_child(); + /** + * @todo TODO: wish I could remember -- document me. + */ + void wait_for_children(bool hang=false); + /** + * @todo TODO: wish I could remember -- document me. + */ + void collect_dead_souls(bool actively=false); + }; + +} + +#endif /* __SITECING_PROCESS_MANAGER_H */ diff --git a/include/sitecing/scoreboard.h b/include/sitecing/scoreboard.h new file mode 100644 index 0000000..788f881 --- a/dev/null +++ b/include/sitecing/scoreboard.h @@ -0,0 +1,102 @@ +#ifndef __SITECING_SCOREBOARD_H +#define __SITECING_SCOREBOARD_H + +#include <sys/types.h> + +/** + * @file + * @brief the scoreboard manager. + */ + +/** + * @def MAX_SITECING_SCOREBOARD_SLOTS + * The maximum number of slots scoreboard can hold. + */ +#define MAX_SITECING_SCOREBOARD_SLOTS 512 + +namespace sitecing { + + /** + * The scoreboard slot. + */ + struct scoreboard_slot { + /** + * The state enumeration. + */ + enum _state { + /** + * The slot is free. + */ + state_free = 0, + /** + * The slot is allocated. + */ + state_allocated, + /** + * The process is idle. + */ + state_idle, + /** + * The process is accepting connection. + */ + state_accept, + /** + * The process is processing request. + */ + state_processing + } state; + pid_t pid; + }; + + /** + * The scoreboard manager. + */ + class scoreboard { + /** + * shared memory id. + */ + int shmid; + public: + /** + * Pointer to the scoreboard slots. + */ + scoreboard_slot *slots; + + scoreboard(); + ~scoreboard(); + + /** + * Allocate a scoreboard slot. + * @return the slot number. + */ + int allocate_slot(); + /** + * Free the slot allocated. + * @param slot the slot number. + */ + void free_slot(int slot); + + /** + * Get the pointer to the slot. + * @param slot the slot number. + * @return the pointer. + */ + scoreboard_slot *get_slot(int slot); + /** + * Find the slot corresponding to the process ID. + * @param pid the process id. + * @return the slot number. + */ + int get_slot_by_pid(pid_t pid); + + /** + * Count the slots in the particular state. + * @param state the state. + * @return the number of slots found. + */ + int count_slots(enum scoreboard_slot::_state state=scoreboard_slot::state_free); + }; + +} + +#endif /* __SITECING_SCOREBOARD_H */ diff --git a/include/sitecing/sitecing_enflesher.h b/include/sitecing/sitecing_enflesher.h new file mode 100644 index 0000000..ad57fb5 --- a/dev/null +++ b/include/sitecing/sitecing_enflesher.h @@ -0,0 +1,65 @@ +#ifndef __SITECING_SITECING_ENFLESHER_H +#define __SITECING_SITECING_ENFLESHER_H + +#include <fstream> +#include <string> +using namespace std; + +/** + * @file + * @brief The preprocessed source builder. + */ + +#ifndef sitecing_enflesher_flexlexer_once +#define sitecing_enflesher_flexlexer_once +#undef yyFlexLexer +#define yyFlexLexer sitecing_enflesherFlexLexer +#include <FlexLexer.h> +#undef yyFlexLexerOnce +#endif + +class sitecing_parser; +/** + * The enfleshing of the skeleton file according to the in-memory parsed + * component source. + */ +class sitecing_enflesher : public sitecing_enflesherFlexLexer { + public: + /** + * It is time to anchor output with the #line directive. + */ + bool anchor_time; + /** + * @todo TODO: wish I could remember -- document me. + */ + bool anchoraged; + /** + * The reference to the parser object containg the parsed source. + */ + sitecing_parser& parser; + /** + * The output stream. + */ + ofstream outs; + + /** + * @param p The parser object containing preparsed data. + */ + sitecing_enflesher(sitecing_parser& p) + : parser(p), anchor_time(true) { } + + /** + * Do the job. + */ + void enflesh(); + + virtual void LexerOutput(const char *buf,int size); + virtual int yylex(); + + /** + * Put a #line anchor into output. + */ + void anchor(); +}; + +#endif /* __SITECING_SITECING_ENFLESHER_H */ diff --git a/include/sitecing/sitecing_exception.h b/include/sitecing/sitecing_exception.h new file mode 100644 index 0000000..bf475ac --- a/dev/null +++ b/include/sitecing/sitecing_exception.h @@ -0,0 +1,103 @@ +#ifndef __SITECING_SITECING_EXCEPTION_H +#define __SITECING_SITECING_EXCEPTION_H + +#include <konforka/exception.h> + +/** + * @file + * @brief The site-C-ing specific exception. + */ + +namespace sitecing { + + /** + * The component failed to compile. + */ + class compile_error : public konforka::exception { + public: + /** + * The component path + */ + string component_path; + + /** + * @param w the message. + * @param cp component path. + */ + compile_error(const string& w,const string& cp) + : konforka::exception(NOCODEPOINT,w), component_path(cp) { } + /** + * @param whe point in code. + * @param wha the message. + * @param cp component path. + */ + compile_error(const string &whe,const string& wha,const string& cp) + : konforka::exception(whe,wha), component_path(cp) { } + /** + * @param fi the file name where the exception is thrown from. + * @param fu the function name where the exception originates from. + * @param l the line number where the exception originates from. + * @param cp component path. + */ + compile_error(const string &fi,const string& fu,int l,const string& w,const string& cp) + : konforka::exception(fi,fu,l,w), component_path(cp) { } + ~compile_error() throw() { } + }; + + /** + * Failed to preprocess component source. + */ + class preprocessor_error : public konforka::exception { + public: + /** + * Component name. + */ + string component_name; + /** + * The line number of the source code where the error occured. + */ + int line_number; + + /** + * @param fi file name where the exception originates from. + * @param fu the function name where the exception originates from. + * @param l the line number where the exception originate from. + * @param w the error message. + * @param cn the component name. + * @param ln the line of the component source where the error occured. + */ + preprocessor_error(const string& fi,const string& fu,int l,const string& w,const string& cn,int ln) + : konforka::exception(fi,fu,l,w), component_name(cn), line_number(ln) { } + /** + * @param fi file name where the exception originates from. + * @param fu the function name where the exception originates from. + * @param l the line number where the exception originate from. + * @param w the error message. + * @param cn the component name. + */ + preprocessor_error(const string& fi,const string& fu,int l,const string& w,const string& cn) + : konforka::exception(fi,fu,l,w), component_name(cn), line_number(-1) { } + /** + * @param fi file name where the exception originates from. + * @param fu the function name where the exception originates from. + * @param l the line number where the exception originate from. + * @param w the error message. + * @param ln the line of the component source where the error occured. + */ + preprocessor_error(const string& fi,const string& fu,int l,const string& w,int ln) + : konforka::exception(fi,fu,l,w), line_number(ln) { } + /** + * @param fi file name where the exception originates from. + * @param fu the function name where the exception originates from. + * @param l the line number where the exception originate from. + * @param w the error message. + */ + preprocessor_error(const string& fi,const string& fu,int l,const string& w) + : konforka::exception(fi,fu,l,w), line_number(-1) { } + + ~preprocessor_error() throw() {} + }; + +} + +#endif /* __SITECING_SITECING_EXCEPTION_H */ diff --git a/include/sitecing/sitecing_interface.h b/include/sitecing/sitecing_interface.h new file mode 100644 index 0000000..0cba2bb --- a/dev/null +++ b/include/sitecing/sitecing_interface.h @@ -0,0 +1,40 @@ +#ifndef __SITECING_SITECING_INTERFACE_H +#define __SITECING_SITECING_INTERFACE_H + +#include <ostream> + +/** + * @file + * @brief The sitecing_interface call declaration. + */ + +namespace sitecing { + using namespace std; + + /** + * @brief the interface to site-C-ing. + * + * The basic class used to convey communications between the component and + * the sitecing core. + */ + class sitecing_interface { + public: + /** + * Pointer to the output stream. + */ + ostream *out; + + /** + * The default constructor doesn't do much. + */ + sitecing_interface() : out(0) {} + /** + * The constructor initializes the output stream pointer. + * @param o the value to initialize the output stream pointer with. + */ + sitecing_interface(ostream* o) : out(o) {} + }; + +} + +#endif /* __SITECING_SITECING_INTERFACE_H */ diff --git a/include/sitecing/sitecing_interface_cgi.h b/include/sitecing/sitecing_interface_cgi.h new file mode 100644 index 0000000..cab947c --- a/dev/null +++ b/include/sitecing/sitecing_interface_cgi.h @@ -0,0 +1,62 @@ +#ifndef __SITECING_SITECING_INTERFACE_CGI_H +#define __SITECING_SITECING_INTERFACE_CGI_H + +#include <sstream> +#include <string> +#include <map> +#include "kingate/cgi_gateway.h" +#include "sitecing/sitecing_interface.h" +#include "sitecing/sitespace.h" + +/** + * @file + * @brief The sitecing_interface_cgi class declaration. + */ + +namespace sitecing { + using namespace std; + + /** + * The interface to site-C-ing core for the CGI component. + */ + class sitecing_interface_cgi : public sitecing_interface { + public: + /** + * Pointer to the CGI gateway interface. + */ + kingate::cgi_gateway* cgigw; + /** + * Type for the map of headers to spit out. + */ + typedef map<string,string> headers_t; + /** + * The list of headers to spit out. + */ + headers_t headers; + /** + * Here is where we prebuffer output. + */ + ostringstream prebuffer; + /** + * Pointer to the sitespace object. + */ + sitespace *ss; // XXX: or does it belong to the generic interface? or should this 'generic' interface exist at all? + + /** + * @param s Pointer to the sitespace object. + */ + sitecing_interface_cgi(sitespace *s); + + /** + * @todo TODO: wish I could remember -- document me. + */ + void prepare(kingate::cgi_gateway *cg); + /** + * @todo TODO: wish I could remember -- document me. + */ + void flush(); + + }; +} + +#endif /* __SITECING_SITECING_INTERFACE_CGI_H */ diff --git a/include/sitecing/sitecing_parser.h b/include/sitecing/sitecing_parser.h new file mode 100644 index 0000000..22d716f --- a/dev/null +++ b/include/sitecing/sitecing_parser.h @@ -0,0 +1,326 @@ +#ifndef __SITECING_SITECING_PARSER_H +#define __SITECING_SITECING_PARSER_H + +#include <string> +#include <list> +#include <map> +#include <stdexcept> +using namespace std; + +#include "sitecing/component_factory.h" +using namespace sitecing; + +/** + * @file + * @brief The component source parser. + */ + +#ifndef sitecing_parser_flexlexer_once +#define sitecing_parser_flexlexer_once +#undef yyFlexLexer +#define yyFlexLexer sitecing_parserFlexLexer +#include <FlexLexer.h> +#undef yyFlexLexerOnce +#endif + +/** + * The component source parser. + */ +class sitecing_parser : public sitecing_parserFlexLexer { + public: + /** + * The ancestor class definition. + */ + class ancestor_class { + public: + /** + * The class name. + */ + string name; + /** + * The source component path. + */ + string path; + + /** + * @param n the class name. + * @param p the component path. + */ + ancestor_class(const string& n,const string& p) + : name(n), path(p) { } + }; + /** + * The list of ancestor classes. + */ + typedef list<ancestor_class> ancestor_classes_t; + /** + * The ancestor classes. + */ + ancestor_classes_t ancestor_classes; + /** + * The member variable definition. + */ + class member_variable { + public: + /** + * The member variable type. + */ + string type; + /** + * The member variable name. + */ + string name; + /** + * The member variable is a component. + */ + bool bComponent; + /** + * The variable initializer. + */ + string initializer; + /** + * @todo TODO: wish I could remember -- document me. + */ + bool bTypeOnly; + + /** + * @param t type. + * @param n name. + * @param i initializer. + * @param bc whether it is a component. + * @param bto @todo TODO: @see bTypeOnly. + */ + member_variable(const string& t,const string& n,const string& i,bool bc = false,bool bto = false) + : type(t), name(n), initializer(i), bComponent(bc), bTypeOnly(bto) { } + }; + /** + * The list of member variables. + */ + typedef list<member_variable> member_variables_t; + /** + * Member variables. + */ + member_variables_t member_variables; + /** + * @todo TODO: wish I could remember the details -- document me. + */ + bool have_initializers; + /** + * Whether the component has a constructor defined. + */ + bool have_constructor; + /** + * Member function definition. + */ + class member_function { + public: + /** + * Return type. + */ + string type; + /** + * Function name. + */ + string name; + /** + * Arguments declaration. + */ + string args; + /** + * Function body. + */ + string body; + + /** + * @param t type. + * @param n name. + * @param a arguments. + * @param b body. + */ + member_function(const string& t,const string& n,const string& a,const string& b) + : type(t), name(n), args(a), body(b) { } + }; + /** + * The list of member functions. + */ + typedef list<member_function> member_functions_t; + /** + * Member functions. + */ + member_functions_t member_functions; + /** + * Current mode of operation. + */ + class modus_operandi { + public: + /** + * The state enumeration. + */ + enum modus_t { + /** + * Building the code. + */ + modus_code = 0, + /** + * Ready to do the '<<' thing. + */ + modus_preop, + /** + * Just made a '<<'. + */ + modus_postop, + /** + * Outputting raw output data. + */ + modus_text, + /** + * The number of modes. + */ + modi + }; + /** + * Processing flags enumeration. + */ + enum { + /** + * Eat the comments. + */ + flag_devour_comments = 0x0001, + /** + * Eat whitespace. + */ + flag_devour_whitespace = 0x0002 + }; + /** + * The processing mode. + */ + modus_t modus; + /** + * The processing flags. + */ + int flags; + /** + * Output being built. + */ + string output; + /** + * The type for compound modes. + */ + string _type; + /** + * The last id encountered. + */ + string _lastid; + /** + * The name for compound modes. + */ + string _name; + /** + * The argument declaration. Obviously for member functions. + */ + string _args; + + /** + * @param flags. + * @see flags + */ + modus_operandi(int f = 0) + : modus(modus_code), flags(f) { } + + /** + * Change the processing mode. + */ + void modify(modus_t m); + + /** + * See if we're eating up whitespaces. + */ + bool devour_whitespace() { return flags&flag_devour_whitespace; } + /** + * See if we're eating up the comments. + */ + bool devour_comments() { return flags&flag_devour_comments; } + }; + /** + * The modes stack type. + */ + typedef list<modus_operandi> modi_operandi; + /** + * The modes stack. + */ + modi_operandi modi; + /** + * Input file name. + */ + string input_file; + /** + * Base class name. + */ + string base_class; + /** + * Base class header. + */ + string base_header; + /** + * Component's basename. + * @todo TODO: wish I could remember the details -- document me. + */ + string component_basename; + /** + * The skeleton file name. + */ + string skeleton; + /** + * The component class name. + */ + string class_name; + /** + * Output basename. + * @todo TODO: wish I could remember the details -- document me. + */ + string output_basename; + /** + * Verbatim declaration part. + */ + string decl; + /** + * Verbatim implementation part. + */ + string impl; + /** + * The reference to the component factory object. + */ + component_factory& factory; + + /** + * @param f the component factory. + */ + sitecing_parser(component_factory& f); + + /** + * Preprocess file. + * @param in input file name. + */ + void preprocess(const string& in); + + virtual void LexerOutput(const char *buf,int size); + virtual int yylex(); + + /** + * Retrieve reference to the to of the modes stack. + * @return the reference in question. + */ + modus_operandi& M() { + return modi.front(); + } + /** + * Anchor the output with the #line, if we're not in the text output mode. + */ + void soft_anchor(); + /** + * Anchor the output with the #line directive, changing to the appropriate output mode if needed. + */ + void anchor(); +}; + +#endif /* __SITECING_SITECING_PARSER_H */ diff --git a/include/sitecing/sitecing_util.h b/include/sitecing/sitecing_util.h new file mode 100644 index 0000000..d1a6c4a --- a/dev/null +++ b/include/sitecing/sitecing_util.h @@ -0,0 +1,341 @@ +#ifndef __SITECING_SITECING_UTIL_H +#define __SITECING_SITECING_UTIL_H + +#include <sys/types.h> +#include <string> +#include <konforka/exception.h> + +/** + * @file + * @brief utility classes and functions. + */ + +namespace sitecing { + using namespace std; + + /** + * Base class for utility exceptions. + */ + class utility_error : public konforka::exception { + public: + utility_error(const string& fi,const string& fu,int l,const string& w) + : konforka::exception(fi,fu,l,w) { } + }; + /** + * Restricted sequence encountered. + */ + class utility_restricted_sequence : public utility_error { + public: + utility_restricted_sequence(const string& fi,const string& fu,int l,const string& w) + : utility_error(fi,fu,l,w) { } + }; + /** + * No prefix or suffix found to strip out. + */ + class utility_no_affix : public utility_error { + public: + utility_no_affix(const string& fi,const string& fu,int l,const string& w) + : utility_error(fi,fu,l,w) { } + }; + /** + * No prefix to strip found. + */ + class utility_no_prefix : public utility_no_affix { + public: + utility_no_prefix(const string& fi,const string& fu,int l,const string& w) + : utility_no_affix(fi,fu,l,w) { } + }; + /** + * No suffix to strip found. + */ + class utility_no_suffix : public utility_no_affix { + public: + utility_no_suffix(const string& fi,const string& fu,int l,const string& w) + : utility_no_affix(fi,fu,l,w) { } + }; + + /** + * Went up beyond root. + * @todo TODO: wish I could remember the details -- document me. + */ + class utility_beyond_root : public utility_error { + public: + utility_beyond_root(const string& fi,const string& fu,int l,const string& w) + : utility_error(fi,fu,l,w) { } + }; + + /** + * The file lock object. Released at the object destruction. + */ + class file_lock { + public: + /** + * The file descriptor. + */ + int fd; + + file_lock() + : fd(-1) { } + /** + * @param f file name. + */ + file_lock(const string& f) + : fd(-1) { lock(f); } + ~file_lock() { unlock(); } + + /** + * Do lock. + * @param f file name. + */ + void lock(const string& f); + /** + * @todo TODO: wish I could remember the details -- document me. + */ + void lock(); + /** + * Release the lock obtained. + */ + void unlock(); + }; + + /** + * The pid file. Removed at object destruction. + */ + class pid_file { + public: + /** + * The file name. + */ + string file_name; + /** + * Do we unlink the file after we're done? + */ + bool unlink_pid; + + pid_file() + : unlink_pid(false) { } + ~pid_file() { unlink(); } + + /** + * @param f file name. + * @param u whether we want to unlink the file. + */ + void set(const string& f,bool u=true); + /** + * Unlink the file if we wanted to in the first place. + */ + void unlink(); + }; + + /** + * The semaphore object. + */ + class semaphore { + public: + /** + * The semaphore id. + */ + int semid; + + semaphore() + : semid(-1) { } + /** + * @param sid semaphore id. + */ + semaphore(int sid) + : semid(sid) { } + ~semaphore() { + deinit(); + } + + /** + * Init semaphore. + */ + void init(); + /** + * Undo the init. + */ + void deinit(); + + /** + * Semaphore on. + */ + void on(); + /** + * Semaphore off. + */ + void off(); + }; + + /** + * The semaphor lock object, released at object destruction. + */ + class semaphore_lock { + public: + /** + * Pointer to the semaphore we're operating on. + */ + semaphore* sem; + /** + * Whether it is locked. + */ + bool locked; + + semaphore_lock() + : sem(NULL), locked(false) {} + /** + * @param s pointer to the semaphore. + * @param l lock at creation? + */ + semaphore_lock(semaphore* s,bool l=true) + : sem(s), locked(false) { + if(l) lock(); + } + /** + * @param s reference to the semaphore. + * @param l lock at creation? + */ + semaphore_lock(semaphore& s,bool l=true) + : sem(&s), locked(false) { + if(l) lock(); + } + + ~semaphore_lock() { + unlock(); + } + + /** + * Lock it. + */ + void lock(); + /** + * Unlock it. + */ + void unlock(); + }; + + /** + * normalize_path options enumeration. + * @see normalize_path() + */ + enum normalize_path_options { + /** + * Restrict the /../ sequence. + */ + restrict_dotdot = 1, + /** + * Strip out the leading slash. + */ + strip_leading_slash = 2, + /** + * Strip out the trailing slash. + */ + strip_trailing_slash = 4 + }; + /** + * combine_path options enumeration. + * @see combine_path() + */ + enum combine_path_options { + /** + * The origin is file. Otherwise it is directory. + */ + origin_is_file = 1, + /** + * Fail if we've gone up beyond root. + */ + fail_beyond_root = 2 + }; + + /** + * Normalize pathname by stripping duplicate slashes, etc. + * @param path the path name. + * @param opts options. + * @return the normalized path. + * @see normalize_path_options + * @todo TODO: document exceptions. + */ + string normalize_path(const string& path,int opts=(restrict_dotdot|strip_trailing_slash)); + /** + * Strip prefix from the string. + * @param str the string. + * @param prefix prefix to strip. + * @return the string without prefix. + * @todo TODO: document exceptions. + */ + string strip_prefix(const string& str,const string& prefix); + /** + * Strip suffix from the string. + * @param str the string. + * @param suffix suffix to strip. + * @return the string without suffix. + * @todo TODO: document exceptions. + */ + string strip_suffix(const string& str,const string& suffix); + /** + * Get the directory part of the filename. + * @param filename the full file name. + * @return the directory part. + */ + string dir_name(const string& filename); + /** + * Combine path with the relative path. + * @param origin the origin. + * @param relative relative path to combine origin with. + * @param opts options. + * @return the pathc combined. + * @see combine_path_options + * @todo TODO: document exceptions. + */ + string combine_path(const string& origin,const string& relative,int opts=origin_is_file); + + /** + * Create directory and parent directories if needed. + * @param path the pathname. + * @param mode the mode for newly created directories. + */ + void make_path(const string& path,mode_t mode); + + /** + * Change to the directory and pop back at object's destruction (e.g. when + * the object goes out of scope). + */ + class auto_chdir { + public: + /** + * Saved working directory. + */ + string saved_pwd; + /** + * Whether we want to change back automatically. + */ + bool autopop; + + auto_chdir() + : autopop(false) { } + /** + * @param td destination path. + * @param ap automatically come back? + */ + auto_chdir(const string& td,bool ap=true) + : autopop(false) { pushdir(td,ap); } + ~auto_chdir() { + if(autopop) + popdir(); + } + + /** + * Change into directory. + * @param td the directory. + * @param ap automaticall pop back? + */ + void pushdir(const string& td,bool ap=true); + /** + * Change to the saved directory. + */ + void popdir(); + }; + +} + +#endif /* __SITECING_SITECING_UTIL_H */ diff --git a/include/sitecing/sitespace.h b/include/sitecing/sitespace.h new file mode 100644 index 0000000..38fafe4 --- a/dev/null +++ b/include/sitecing/sitespace.h @@ -0,0 +1,76 @@ +#ifndef __SITECING_SITESPACE_H +#define __SITECING_SITESPACE_H + +#include <string> +#include <map> +#include <list> +#include "sitecing/component_factory.h" +#include "sitecing/component_so.h" +#include "sitecing/configuration.h" + +/** + * @file + * @brief The sitespace class declaration. + */ + +namespace sitecing { + using namespace std; + + /** + * The class responsible for handling the whole environment (as far as I can + * remember). + */ + class sitespace { + public: + /** + * The type for the map of components from the component name/path + * to the loaded component objects. + */ + typedef map<string,component_so*> components_t; + /** + * The type for listing the components. + */ + typedef list<component_so*> sentenced_t; + /** + * The main configuration object. + */ + configuration& config; + /** + * The components producing factory. + */ + component_factory factory; + /** + * The components loaded. + */ + components_t components; + /** + * The list of components sentenced to death. + */ + sentenced_t sentenced; + + /** + * Create an object in accordance with the configuration parsed. + * @param c the coniguration container. + */ + sitespace(configuration& c); + ~sitespace(); + + /** + * Fetch the component, providing it with the interface object + * pointer. + * @param c the component name. + * @param scif the interface object. + * @return the component fetches. + */ + so_component fetch(const string& c,sitecing_interface* scif); + + private: + /** + * Execute the death sentence as much as we can. + */ + void execute_sentenced(); + }; + +} + +#endif /* __SITECING_SITESPACE_H */ diff --git a/include/sitecing/util.h b/include/sitecing/util.h new file mode 100644 index 0000000..5750ab6 --- a/dev/null +++ b/include/sitecing/util.h @@ -0,0 +1,148 @@ +#ifndef __SITECING_UTIL_H +#define __SITECING_UTIL_H + +#include <ostream> +#include <string> +#include "sitecing/acomponent.h" + +/** + * @file + * @brief more or less non-internal utility classes and functions. + */ + +namespace sitecing { + using namespace std; + + /** + * the html_escape options enumeration. + */ + enum html_escape_options { + /** + * Turn spaces into + */ + html_escape_nbsp = 0x0001, + /** + * Turn newlines into <br/> or <br>. + */ + html_escape_br = 0x0002, + /** + * Turn quotes to " + */ + html_escape_quot = 0x0004, + /** + * Do not put '/' into <br/> consruct. + */ + html_escape_br_noslash = 0x0008 + }; + /** + * Escape string suitable for html output. + * @param str the string. + * @param flags options. + * @return the string escaped. + * @see html_escape_options + */ + string html_escape(const string& str,int flags=html_escape_br); + + /** + * The output string checkpoint object, letting one to rollback output. + */ + class checkpoint { + public: + /** + * The object's death will enumeration. + */ + enum will_t { + /** + * The stream is to be rolled back at object destruction. + */ + will_rollback, + /** + * The stream is not to be rolled back at object destruction. + */ + will_commit, + /** + * The object will die intestate. What's the point then? + */ + will_intestate + }; + /** + * The output stream in question. + */ + ostream* stream; + /** + * The point at which objhect was created. + */ + ostream::pos_type point; + /** + * The last will. + */ + will_t last_will; + + /** + * @param s reference to the stream. + * @param lw the last will. + */ + checkpoint(ostream& s, will_t lw=will_rollback) + : stream(&s), last_will(lw) { set(); } + /** + * @param s pointer to the stream. + * @param lw the last will. + */ + checkpoint(ostream* s, will_t lw=will_rollback) + : stream(s), last_will(lw) { set(); } + /** + * @param s reference to the sitecing interface where to get output + * stream from. + * @param lw the last will. + */ + checkpoint(sitecing_interface& s, will_t lw=will_rollback) + : stream(s.out), last_will(lw) { set(); } + /** + * @param s pointer to the sitecing interface where to get output + * stream from. + * @param lw the last will. + */ + checkpoint(sitecing_interface* s, will_t lw=will_rollback) + : stream(s->out), last_will(lw) { set(); } + /** + * @param c reference to the component from which the output stream + * is obtained. + * @param lw the last will. + */ + checkpoint(acomponent& c, will_t lw=will_rollback) + : stream(c.__SCIF->out), last_will(lw) { set(); } + /** + * @param c pointer to the component from which the output stream is + * obtained. + * @param lw the last will. + */ + checkpoint(acomponent* c, will_t lw=will_rollback) + : stream(c->__SCIF->out), last_will(lw) { set(); } + ~checkpoint() { + if(last_will==will_rollback) + rollback(); + } + + /** + * Set the possible rolback point to the current position in stream. + */ + void set(); + /** + * Make or change will. + */ + void make_will(will_t lw); + /** + * Rollback the output made so far. In case of rollback will + * change to intestate. + */ + void rollback(); + /** + * Commit output so far. In case of rollback will, change to + * intestate. + */ + void commit(); + }; + +} + +#endif /* __SITECING_UTIL_H */ diff --git a/lib/.gitignore b/lib/.gitignore new file mode 100644 index 0000000..8137060 --- a/dev/null +++ b/lib/.gitignore @@ -0,0 +1,9 @@ +Makefile.in +sitecing_enflesher.cc +sitecing_parser.cc +.libs +.deps +Makefile +*.o +*.lo +*.la diff --git a/lib/Makefile.am b/lib/Makefile.am new file mode 100644 index 0000000..53d8182 --- a/dev/null +++ b/lib/Makefile.am @@ -0,0 +1,22 @@ +lib_LTLIBRARIES = libsitecing.la +noinst_HEADERS = pch.h + +INCLUDES = -I${top_srcdir}/include -I${top_builddir} ${KINGATE_CFLAGS} ${DOTCONF_CFLAGS} \ + ${PCREPP_CFLAGS} +AM_CPPFLAGS = -D__SC_DEFAULT_SKELETON=\"${pkgdatadir}/component.skel\" +AM_LFLAGS = -olex.yy.c + +libsitecing_la_SOURCES = \ + sitecing_parser.ll sitecing_enflesher.ll \ + sitecing_interface_cgi.cc \ + acomponent.cc \ + cgi_component.cc \ + component_so.cc \ + file_factory.cc component_factory.cc \ + sitespace.cc \ + configuration.cc \ + util.cc sitecing_util.cc \ + scoreboard.cc \ + process_manager.cc +libsitecing_la_LDFLAGS = \ + -version-info 1:0:0 diff --git a/lib/acomponent.cc b/lib/acomponent.cc new file mode 100644 index 0000000..8dfeee4 --- a/dev/null +++ b/lib/acomponent.cc @@ -0,0 +1,47 @@ +#ifdef USE_PCH + #include "pch.h" +#else + #include <cstdarg> + #include <fstream> + #include <konforka/exception.h> + using namespace std; + #include "sitecing/acomponent.h" +#endif + +namespace sitecing { + + acomponent::acomponent() + : __SCIF(NULL) { + } + acomponent::~acomponent() { + } + + void acomponent::__set_interface(sitecing_interface* scif) { + sitecing_interface *o = __SCIF; + __SCIF = scif; + if(o!=scif) { + __on_change_interface(o); + __do_imports(); + __on_imports(); + } + } + + void acomponent::__on_change_interface(sitecing_interface *oscif) { } + void acomponent::__do_imports() { } + void acomponent::__on_imports() { } + + void acomponent::run(int _magic,...) { + va_list va; + va_start(va,_magic); + main(_magic,va); + va_end(va); + } + + + void acomponent::pass_file_through(const char *fn) { + ifstream ifs(fn,ios::in|ios::binary); + if(!ifs) + throw konforka::exception(CODEPOINT,"failed to open file"); + (*(__SCIF->out)) << ifs.rdbuf(); + } +} diff --git a/lib/cgi_component.cc b/lib/cgi_component.cc new file mode 100644 index 0000000..b5c4bee --- a/dev/null +++ b/lib/cgi_component.cc @@ -0,0 +1,30 @@ +#ifdef USE_PCH + #include "pch.h" +#else + #include "sitecing/cgi_component.h" +#endif + +namespace sitecing { + + cgi_component::cgi_component() + : __CGI(NULL) { + } + cgi_component::~cgi_component() { + } + + void cgi_component::__set_interface(sitecing_interface* scif) { + acomponent::__set_interface(scif); + kingate::cgi_gateway *oc = __CGI; + __CGI = __SCIF?__SCIF->cgigw:NULL; + if(__CGI!=oc) + __on_change_CGI(oc); + } + void cgi_component::__on_change_interface(sitecing_interface *o) { + acomponent::__on_change_interface(o); // But it's a no-op + // TODO: do something about runtime type check, maybe? + __SCIF = (sitecing_interface_cgi*)acomponent::__SCIF; + } + void cgi_component::__on_change_CGI(kingate::cgi_gateway *o) { } + void cgi_component::__on_imports() { } + +} diff --git a/lib/component_factory.cc b/lib/component_factory.cc new file mode 100644 index 0000000..bcf19f2 --- a/dev/null +++ b/lib/component_factory.cc @@ -0,0 +1,279 @@ +#ifdef USE_PCH + #include "pch.h" +#else + #include <sys/types.h> + #include <sys/stat.h> + #include <unistd.h> + #include <sys/wait.h> + #include <fcntl.h> + #include <iostream> + #include <fstream> + #include <stdexcept> + #include <vector> + using namespace std; + #include "sitecing/component_factory.h" + #include "sitecing/sitecing_util.h" + #include "sitecing/sitecing_parser.h" + #include "sitecing/sitecing_exception.h" +#endif + +namespace sitecing { + + static const char *pp_targets[] = { ".cc", ".h", ".imports", ".classname", ".baseclassname", ".ancestors" }; + + component_factory::component_factory(configuration& c) + : config(c), + root_source(normalize_path(c.root_source,strip_trailing_slash)+'/'), + root_intermediate(normalize_path(c.root_intermediate,strip_trailing_slash)+'/'), + root_so(normalize_path(c.root_so,strip_trailing_slash)+'/') { + } + + void component_factory::get_dependencies(const string& dst,file_list_t& deps) { + deps.clear(); + string dp = normalize_path(dst,strip_trailing_slash); + // source documents + try { // XXX: or just compare it off? + string noro = strip_prefix(dp,root_source); + return; + }catch(utility_no_affix& una) { + } + // .so binaries + try { + string noso = strip_suffix(dp,".so"); + string noro = strip_prefix(noso,root_so); + deps.push_back(root_intermediate+noro+".cc"); + config_options *co_cpp_deps = config.lookup_config(noro,config_options::flag_cpp_deps); + if( (!co_cpp_deps) || co_cpp_deps->cpp_deps) { + ifstream df((root_intermediate+noro+".d").c_str(),ios::in); + if(df.good()) { + string str; + while(!df.eof()) { + df >> str; + if(str.find_first_of("\\:")==string::npos) + deps.push_back(combine_path(config.root_source+noro,str)); + } + } + } + config_options *co_so_deps = config.lookup_config(noro,config_options::flag_so_deps); + if(co_so_deps) { + for(list<string>::const_iterator i=co_so_deps->so_deps.begin();i!=co_so_deps->so_deps.end();++i) + deps.push_back(*i); + } + return; + }catch(utility_no_prefix& unp) { + throw konforka::exception(CODEPOINT,"component is outside of component root"); + }catch(utility_no_suffix& uns) { + } + // preprocessor targets + for(int ppt=0;ppt<sizeof(pp_targets)/sizeof(*pp_targets);ppt++) { + try { + string nos = strip_suffix(dp,pp_targets[ppt]); + string noro = strip_prefix(nos,root_intermediate); + deps.push_back(root_source+noro); + ifstream imports((root_intermediate+noro+".imports").c_str(),ios::in); + if(imports.good()) { + string str; + while(!imports.eof()) { + imports >> str; + if(!str.empty()) + deps.push_back(root_intermediate+str+".classname"); + } + } + ifstream ancestors((root_intermediate+noro+".ancestors").c_str(),ios::in); + if(ancestors.good()) { + string str; + while(!ancestors.eof()) { + ancestors >> str; + if(!str.empty()) + deps.push_back(root_intermediate+str+".classname"); + } + } + config_options *co_intermediate_deps = config.lookup_config(noro,config_options::flag_intermediate_deps); + if(co_intermediate_deps) { + for(list<string>::const_iterator i=co_intermediate_deps->intermediate_deps.begin();i!=co_intermediate_deps->intermediate_deps.end();++i) + deps.push_back(*i); + } + return; + }catch(utility_no_affix& una) { + // do nothing. must be a cpp dependency. + } + } + } + + bool component_factory::is_uptodate(const string& dst,file_list_t *deps) { + string dp = normalize_path(dst,strip_trailing_slash); + // XXX: or just compare it off, instead of throwing things around. + try { + strip_prefix(dp,root_intermediate); + return file_factory::is_uptodate(dst,deps); + }catch(utility_no_prefix& unp) { + } + try { + strip_prefix(dp,root_so); + return file_factory::is_uptodate(dst,deps); + }catch(utility_no_prefix& unp) { + } + return true; + } + + void component_factory::build(const string& dst) { + string dp = normalize_path(dst,strip_trailing_slash); + // sources + try { + string noro = strip_prefix(dp,root_source); + // building the sources is left up to developer + return; + }catch(utility_no_prefix& unp) { + } + // .so files + try { + string noso = strip_suffix(dp,".so"); + string noro = strip_prefix(noso,root_so); + string cc = root_intermediate+noro+".cc"; + if(access(cc.c_str(),R_OK)) + throw konforka::exception(CODEPOINT,string("can't access preprocessed component code (")+cc+")"); + make_path(dir_name(root_so+noro),0755); + string pwd = dir_name(root_source+noro); + auto_chdir dir_changer(pwd); + file_lock lock_source(root_intermediate+noro+".lock"); + file_lock lock_so(root_so+noro+".so.lock"); + int stdO = open((root_intermediate+noro+".stdout").c_str(),O_CREAT|O_TRUNC|O_WRONLY,0664); + if(stdO<0) + throw konforka::exception(CODEPOINT,"failed to open/create compiler stdout"); + int stdE = open((root_intermediate+noro+".stderr").c_str(),O_CREAT|O_TRUNC|O_WRONLY,0664); + if(stdE<0) { + close(stdO); + throw konforka::exception(CODEPOINT,"failed to open/create compiler's stderr"); + } + list<string> args; + config_options *co_cpp_flags = config.lookup_config(noro,config_options::flag_cpp_flags); + if(co_cpp_flags) { + args.insert(args.end(),co_cpp_flags->cpp_flags.begin(),co_cpp_flags->cpp_flags.end()); + } + config_options *co_ld_flags = config.lookup_config(noro,config_options::flag_ld_flags); + if(co_ld_flags) { + args.insert(args.end(),co_ld_flags->ld_flags.begin(),co_ld_flags->ld_flags.end()); + } + // TODO: maybe move it to separare config option like CoreCPPFLags? + args.push_back("-I"+root_intermediate); + args.push_back("-I"+root_source); + args.push_back("-MD"); args.push_back("-MF"); args.push_back(root_intermediate+noro+".d"); + args.push_back("-shared"); + args.push_back("-o"); args.push_back(dp); + args.push_back(cc); + file_list_t ancestors; + get_ancestors(noro,ancestors); + for(file_list_t::const_iterator i=ancestors.begin();i!=ancestors.end();++i) { + string aso=root_so+*i+".so"; + make(aso); + args.push_back(aso); + } + // TODO: "g++" configurable + int rv = execute("g++",args,stdO,stdE); + if(! (WIFEXITED(rv) && !WEXITSTATUS(rv)) ) + throw compile_error(CODEPOINT,"failed to compile component",noro); + return; + }catch(utility_no_prefix& unp) { + throw konforka::exception(CODEPOINT,"component is outside of component root"); + }catch(utility_no_suffix& uns) { + } + // preprocessor targets + for(int ppt=0;ppt<sizeof(pp_targets)/sizeof(*pp_targets);ppt++) { + try { + string nos = strip_suffix(dp,pp_targets[ppt]); + string noro = strip_prefix(nos,root_intermediate); + string src = root_source+noro; + if(access(src.c_str(),R_OK)) + throw konforka::exception(CODEPOINT,string("can't access component source (")+src+")"); + make_path(dir_name(root_intermediate+noro),0755); + file_lock lock(root_intermediate+noro+".lock"); + sitecing_parser parser(*this); + config_options *co_skeleton = config.lookup_config(noro,config_options::flag_skeleton); + if(co_skeleton) + parser.skeleton = co_skeleton->skeleton; + static const char *id_chars = "abcdefghijklmnopqrstuvwxyz0123456789_ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + parser.class_name = normalize_path(noro,strip_leading_slash|strip_trailing_slash); + for(string::size_type illc = parser.class_name.find_first_not_of(id_chars);illc!=string::npos;illc=parser.class_name.find_first_not_of(id_chars,illc)) { + string::size_type lc = parser.class_name.find_first_of(id_chars,illc); + int n = ((lc==string::npos)?parser.class_name.length():lc)-illc; + parser.class_name.replace(illc,n,n,'_'); + } + parser.class_name = "_SCC_"+parser.class_name; + parser.output_basename = nos; + parser.component_basename = noro; + try { + parser.preprocess(src); + }catch(preprocessor_error& pe) { + pe.component_name = noro; + pe.see(CODEPOINT); + throw; + } + return; + }catch(utility_no_affix& una) { + // must be a crap from .d file + } + } + cerr << "ignoring build request for " << dp << endl; + } + + int component_factory::execute(const string& cmd, const list<string>& args,int stdo,int stde) { + // XXX: is it right that we do stdio/stderr tricks outside of the function? + cerr << "executing: " << cmd; + vector<const char*> argv(args.size()+2); + argv[0]=cmd.c_str(); + int an = 1; + for(list<string>::const_iterator i=args.begin();i!=args.end();i++) { + cerr << " " << *i ; + argv[an++] = i->c_str(); + } + cerr << endl; + argv[an++]=NULL; + pid_t pid = vfork(); + if(pid==-1) { + close(stdo); close(stde); + throw konforka::exception(CODEPOINT,"failed to vfork()"); + } + if(!pid) { + // child + if(dup2(stdo,1)!=1) + _exit(-1); + if(dup2(stde,2)!=2) + _exit(-1); + close(0); + execvp(cmd.c_str(),(char**)&argv.front()); + _exit(-1); + } + // parent + close(stdo); close(stde); + int rv; + if(waitpid(pid,&rv,0)<0) + throw konforka::exception(CODEPOINT,"failed to waitpid()"); + return rv; + } + + string component_factory::get_classname(const string& component) { + string cn = root_intermediate+normalize_path(component,strip_trailing_slash|strip_leading_slash)+".classname"; + make(cn); + ifstream ifs(cn.c_str()); + if(!ifs.good()) + throw konforka::exception(CODEPOINT,"failed to access component .classname"); + ifs >> cn; + return cn; + } + + void component_factory::get_ancestors(const string& component,file_list_t& rv) { + string cn = root_intermediate+normalize_path(component,strip_trailing_slash|strip_leading_slash)+".ancestors"; + make(cn); + ifstream ifs(cn.c_str()); + if(!ifs.good()) + throw konforka::exception(CODEPOINT,"filed to access component .ancestors"); + rv.clear(); + while(!ifs.eof()) { + string a; + ifs >> a; + if(!a.empty()) + rv.push_back(a); + } + } + +} diff --git a/lib/component_so.cc b/lib/component_so.cc new file mode 100644 index 0000000..57cce01 --- a/dev/null +++ b/lib/component_so.cc @@ -0,0 +1,112 @@ +#ifdef USE_PCH + #include "pch.h" +#else + #include <unistd.h> + #include <dlfcn.h> + #include <iostream> + #include <cassert> + #include <stdexcept> + using namespace std; + #include "sitecing/component_so.h" + #include "sitecing/sitecing_util.h" +#endif + +namespace sitecing { + + /* + * component_so + */ + + component_so::component_so(const string& soname) + : dl(NULL), sofile(soname) { + if(stat(sofile.c_str(),&stso)) + throw konforka::exception(CODEPOINT,"failed to stat() shared object"); + file_lock lock(sofile+".lock"); + dl = dlopen(sofile.c_str(),RTLD_LAZY); + lock.unlock(); + if(!dl) + throw konforka::exception(CODEPOINT,"failed to dlopen: "+string(dlerror())); + egg = (egg_t)dlsym(dl,"_egg"); + if(!egg) + throw konforka::exception(CODEPOINT,"failed to dlsym: "+string(dlerror())); + } + component_so::~component_so() { + for(free_chickens_t::iterator i=chickens_free.begin();i!=chickens_free.end();i++) + delete *i; + chickens_free.clear(); + if(!chickens_used.empty()) + throw konforka::exception(CODEPOINT,"attempt to destroy the component in use"); + dlclose(dl); + } + + bool component_so::is_uptodate() const { + struct stat st; + if(stat(sofile.c_str(),&st)) + throw konforka::exception(CODEPOINT,"failed to stat() shared object"); + return stso.st_mtime==st.st_mtime; + } + + acomponent* component_so::allocate_chicken() { + acomponent *rv; + if(!chickens_free.empty()) { + rv = chickens_free.front(); + chickens_free.pop_front(); + }else{ + rv = (*egg)(); + } + assert(chickens_used.find(rv)==chickens_used.end()); + chickens_used[rv]=1; + return rv; + } + + void component_so::allocate_chicken(acomponent* ac) { + used_chickens_t::iterator i = chickens_used.find(ac); + if(i!=chickens_used.end()) { + i->second++; + }else{ + free_chickens_t::iterator i; + for(i=chickens_free.begin();*i!=ac && i!=chickens_free.end();i++); + if(i==chickens_free.end()) + throw konforka::exception(CODEPOINT,"hens rarely adopt chickens"); + chickens_free.erase(i); + chickens_used[ac]=1; + } + } + + void component_so::deallocate_chicken(acomponent* ac) { + used_chickens_t::iterator i = chickens_used.find(ac); + if(i==chickens_used.end()) + throw konforka::exception(CODEPOINT,"you can't deallocate what is not allocated"); + i->second--; + if(i->second>0) + return; + chickens_used.erase(i); + chickens_free.push_front(ac); + } + + /* + * so_component + */ + + so_component::so_component(component_so *h,sitecing_interface *scif) + : hen(h), ac(NULL) { + if(!hen) + throw konforka::exception(CODEPOINT,"can't get an egg from the null-hen"); + ac = hen->allocate_chicken(); + ac->__set_interface(scif); + } + + void so_component::attach(component_so *h,acomponent *a) { + detach(); hen = h; ac = a; + if(!ac) + throw konforka::exception(CODEPOINT,"trying to clone null-chicken"); + if(!hen) + throw konforka::exception(CODEPOINT,"trying to clone orphan chicken"); + hen->allocate_chicken(ac); + } + void so_component::detach() { + if(hen && ac) + hen->deallocate_chicken(ac); + } + +} diff --git a/lib/configuration.cc b/lib/configuration.cc new file mode 100644 index 0000000..4ee1526 --- a/dev/null +++ b/lib/configuration.cc @@ -0,0 +1,474 @@ +#ifdef USE_PCH + #include "pch.h" +#else + #include <unistd.h> + #include <fnmatch.h> + #include <cassert> + #include <stdexcept> + using namespace std; + #include <dotconf.h> + #include "sitecing/configuration.h" + #include "sitecing/sitecing_util.h" + #include "sitecing/scoreboard.h" +#endif + +namespace sitecing { + + configuration::configuration() + : flags(0), autobuild(false) { } + configuration::configuration(const string& cfile,bool ab) + : flags(0), autobuild(ab) { + parse(cfile); + } + + enum dc_ctx { + DCC_ROOT = 1, + DCC_PATH = 2, + DCC_SCRC = 4 + }; + struct dc_context { + dc_ctx ctx; + configuration* cf; + list<config_options*> co; + }; + + static DOTCONF_CB(dco_root_source) { dc_context *dcc = (dc_context*)ctx; + dcc->cf->root_source = cmd->data.str; + dcc->cf->flags |= configuration::flag_root_source; + return NULL; + } + static DOTCONF_CB(dco_root_intermediate) { dc_context *dcc = (dc_context*)ctx; + dcc->cf->root_intermediate = cmd->data.str; + dcc->cf->flags |= configuration::flag_root_intermediate; + return NULL; + } + static DOTCONF_CB(dco_root_so) { dc_context *dcc = (dc_context*)ctx; + dcc->cf->root_so = cmd->data.str; + dcc->cf->flags |= configuration::flag_root_so; + return NULL; + } + static DOTCONF_CB(dco_listen_socket) { dc_context *dcc = (dc_context*)ctx; + dcc->cf->listen_socket = cmd->data.str; + dcc->cf->flags |= configuration::flag_listen_socket; + return NULL; + } + static DOTCONF_CB(dco_rc_file_name) { dc_context *dcc = (dc_context*)ctx; + dcc->cf->rc_file_name = cmd->data.str; + dcc->cf->flags |= configuration::flag_rc_file_name; + return NULL; + } + static DOTCONF_CB(dco_min_children) { dc_context *dcc = (dc_context*)ctx; + if(cmd->data.value>=MAX_SITECING_SCOREBOARD_SLOTS) + return "MinChildren is too big"; + dcc->cf->min_children = cmd->data.value; + dcc->cf->flags |= configuration::flag_min_children; + return NULL; + } + static DOTCONF_CB(dco_max_children) { dc_context *dcc = (dc_context*)ctx; + if(cmd->data.value>=MAX_SITECING_SCOREBOARD_SLOTS) + return "MaxChildren is too big"; + dcc->cf->max_children = cmd->data.value; + dcc->cf->flags |= configuration::flag_max_children; + return NULL; + } + static DOTCONF_CB(dco_min_spare_children) { dc_context *dcc = (dc_context*)ctx; + if(cmd->data.value>=MAX_SITECING_SCOREBOARD_SLOTS) + return "MinSpareChildren is too big"; + dcc->cf->min_spare_children = cmd->data.value; + dcc->cf->flags |= configuration::flag_min_spare_children; + return NULL; + } + static DOTCONF_CB(dco_max_spare_children) { dc_context *dcc = (dc_context*)ctx; + if(cmd->data.value>=MAX_SITECING_SCOREBOARD_SLOTS) + return "MaxSpareChildren is too big"; + dcc->cf->max_spare_children = cmd->data.value; + dcc->cf->flags |= configuration::flag_max_spare_children; + return NULL; + } + static DOTCONF_CB(dco_requests_per_child) { dc_context *dcc = (dc_context*)ctx; + dcc->cf->requests_per_child = cmd->data.value; + dcc->cf->flags |= configuration::flag_requests_per_child; + return NULL; + } + static DOTCONF_CB(dco_multi_process) { dc_context *dcc = (dc_context*)ctx; + dcc->cf->multi_process = cmd->data.value; + dcc->cf->flags |= configuration::flag_multi_process; + return NULL; + } + static DOTCONF_CB(dco_user) { dc_context *dcc = (dc_context*)ctx; + dcc->cf->user = cmd->data.str; + dcc->cf->flags |= configuration::flag_user; + return NULL; + } + static DOTCONF_CB(dco_group) { dc_context *dcc = (dc_context*)ctx; + dcc->cf->group = cmd->data.str; + dcc->cf->flags |= configuration::flag_group; + return NULL; + } + static DOTCONF_CB(dco_chroot) { dc_context *dcc = (dc_context*)ctx; + dcc->cf->chroot = cmd->data.str; + dcc->cf->flags |= configuration::flag_chroot; + return NULL; + } + static DOTCONF_CB(dco_pid_file) { dc_context *dcc = (dc_context*)ctx; + dcc->cf->pid_file = cmd->data.str; + dcc->cf->flags |= configuration::flag_pid_file; + return NULL; + } + static DOTCONF_CB(dco_daemonize) { dc_context *dcc = (dc_context*) ctx; + dcc->cf->daemonize = cmd->data.value; + dcc->cf->flags |= configuration::flag_daemonize; + return NULL; + } + + static DOTCONF_CB(dco_path) { dc_context *dcc = (dc_context*)ctx; + string path = cmd->data.str; + if(path[path.length()-1]=='>') + path.erase(path.length()-1); + // TODO: normalize path + dcc->co.push_front(&(dcc->cf->specs[path])); + dcc->ctx = DCC_PATH; // TODO: stack it, instead + return NULL; + } + static DOTCONF_CB(dco__path) { dc_context *dcc = (dc_context*)ctx; + dcc->co.pop_front(); + assert(dcc->co.size()); + dcc->ctx = DCC_ROOT; // TODO: stack it, instead + return NULL; + } + + static DOTCONF_CB(dco_skeleton) { dc_context *dcc = (dc_context*)ctx; + dcc->co.front()->skeleton = cmd->data.str; + dcc->co.front()->flags |= config_options::flag_skeleton; + return NULL; + } + static DOTCONF_CB(dco_cpp_flags) { dc_context *dcc = (dc_context*)ctx; + for(char **arg=cmd->data.list;*arg;arg++) + dcc->co.front()->cpp_flags.push_back(*arg); + dcc->co.front()->flags |= config_options::flag_cpp_flags; + return NULL; + } + static DOTCONF_CB(dco_ld_flags) { dc_context *dcc = (dc_context*)ctx; + for(char **arg=cmd->data.list;*arg;arg++) + dcc->co.front()->ld_flags.push_back(*arg); + dcc->co.front()->flags |= config_options::flag_ld_flags; + return NULL; + } + static DOTCONF_CB(dco_intermediate_deps) { dc_context *dcc = (dc_context*) ctx; + for(char **arg=cmd->data.list;*arg;arg++) + dcc->co.front()->intermediate_deps.push_back(*arg); + dcc->co.front()->flags |= config_options::flag_intermediate_deps; + return NULL; + } + static DOTCONF_CB(dco_so_deps) { dc_context *dcc = (dc_context*) ctx; + for(char **arg=cmd->data.list;*arg;arg++) + dcc->co.front()->so_deps.push_back(*arg); + dcc->co.front()->flags |= config_options::flag_so_deps; + return NULL; + } + static DOTCONF_CB(dco_build) { dc_context *dcc = (dc_context*)ctx; + dcc->co.front()->build = cmd->data.value; + dcc->co.front()->flags |= config_options::flag_build; + return NULL; + } + static DOTCONF_CB(dco_cpp_deps) { dc_context *dcc = (dc_context*)ctx; + dcc->co.front()->cpp_deps = cmd->data.value; + dcc->co.front()->flags |= config_options::flag_cpp_deps; + return NULL; + } + static DOTCONF_CB(dco_exception_handler) { dc_context *dcc = (dc_context*)ctx; + dcc->co.front()->exception_handler = cmd->data.str; + dcc->co.front()->flags |= config_options::flag_exception_handler; + return NULL; + } + static DOTCONF_CB(dco_http_status_handler) { dc_context *dcc = (dc_context*)ctx; + if(cmd->arg_count!=2) + return "Invalid number of arguments"; + dcc->co.front()->http_status_handlers[cmd->data.list[0]] = cmd->data.list[1]; + dcc->co.front()->flags |= config_options::flag_http_status_handlers; + return NULL; + } + static DOTCONF_CB(dco_action) { dc_context *dcc = (dc_context*)ctx; + if(cmd->arg_count<2) + return "Invalid number of arguments"; + try { + char **arg=cmd->data.list; + dcc->co.front()->action_handlers.push_back(config_options::action_handler_t(arg[0],arg[1])); + for(arg+=2;*arg;arg++) + dcc->co.front()->action_handlers.back().args.push_back(*arg); + dcc->co.front()->flags |= config_options::flag_action_handlers; + }catch(exception& e) { + return "Error processing Action directive"; // XXX: could be done better + } + return NULL; + } + static DOTCONF_CB(dco_auto_build_files) { dc_context *dcc = (dc_context*)ctx; + if(!( dcc->cf && dcc->cf->autobuild)) + return NULL; + for(char **arg=cmd->data.list;*arg;arg++) + dcc->co.front()->auto_build_files.push_back(*arg); + dcc->co.front()->flags |= config_options::flag_auto_build_files; + return NULL; + } + + static const configoption_t dc_options[] = { + { "RootSource", ARG_STR, dco_root_source, NULL, DCC_ROOT }, + { "RootIntermediate", ARG_STR, dco_root_intermediate, NULL, DCC_ROOT }, + { "RootSO", ARG_STR, dco_root_so, NULL, DCC_ROOT }, + { "ListenSocket", ARG_STR, dco_listen_socket, NULL, DCC_ROOT }, + { "RCFileName", ARG_STR, dco_rc_file_name, NULL, DCC_ROOT }, + { "MinChildren", ARG_INT, dco_min_children, NULL, DCC_ROOT }, + { "MaxChildren", ARG_INT, dco_max_children, NULL, DCC_ROOT }, + { "MinSpareChildren", ARG_INT, dco_min_spare_children, NULL, DCC_ROOT }, + { "MaxSpareChildren", ARG_INT, dco_max_spare_children, NULL, DCC_ROOT }, + { "RequestsPerChild", ARG_INT, dco_requests_per_child, NULL, DCC_ROOT }, + { "MultiProcess", ARG_TOGGLE, dco_multi_process, NULL, DCC_ROOT }, + { "User", ARG_STR, dco_user, NULL, DCC_ROOT }, + { "Group", ARG_STR, dco_group, NULL, DCC_ROOT }, + { "Chroot", ARG_STR, dco_chroot, NULL, DCC_ROOT }, + { "PidFile", ARG_STR, dco_pid_file, NULL, DCC_ROOT }, + { "Daemonize", ARG_TOGGLE, dco_daemonize, NULL, DCC_ROOT }, + { "<Path", ARG_STR, dco_path, NULL, DCC_ROOT }, + { "Skeleton", ARG_STR, dco_skeleton, NULL, DCC_ROOT|DCC_PATH|DCC_SCRC }, + { "CPPFLAGS", ARG_LIST, dco_cpp_flags, NULL, DCC_ROOT|DCC_PATH|DCC_SCRC }, + { "LDFLAGS", ARG_LIST, dco_ld_flags, NULL, DCC_ROOT|DCC_PATH|DCC_SCRC }, + { "Build", ARG_TOGGLE, dco_build, NULL, DCC_ROOT|DCC_PATH|DCC_SCRC }, + { "CPPDeps", ARG_TOGGLE, dco_cpp_deps, NULL, DCC_ROOT|DCC_PATH|DCC_SCRC }, + { "ExceptionHandler", ARG_STR, dco_exception_handler, NULL, DCC_ROOT|DCC_PATH|DCC_SCRC }, + { "HTTPStatusHandler", ARG_LIST, dco_http_status_handler, NULL, DCC_ROOT|DCC_PATH|DCC_SCRC }, + { "IntermediateDeps", ARG_LIST, dco_intermediate_deps, NULL, DCC_ROOT|DCC_PATH|DCC_SCRC }, + { "SODeps", ARG_LIST, dco_so_deps, NULL, DCC_ROOT|DCC_PATH|DCC_SCRC }, + { "Action", ARG_LIST, dco_action, NULL, DCC_ROOT|DCC_PATH|DCC_SCRC }, + { "AutoBuildFiles", ARG_LIST, dco_auto_build_files, NULL, DCC_ROOT|DCC_PATH|DCC_SCRC }, + { "</Path>", ARG_NONE, dco__path, NULL, DCC_PATH }, + LAST_OPTION + }; + + static const char *dc_context_checker(command_t *cmd,unsigned long mask) { + dc_context *dcc = (dc_context*)cmd->context; + if( (mask==CTX_ALL) || ((mask&dcc->ctx)==dcc->ctx) ) + return NULL; + return "misplaced option"; + } + static FUNC_ERRORHANDLER(dc_error_handler) { + throw konforka::exception(CODEPOINT,string("error parsing config file: ")+msg); + } + + bool loaded_options::is_valid() { + struct stat nst; + if(stat(source_file.c_str(),&nst)) + return false; + if(st.st_mtime!=nst.st_mtime) + return false; + return true; + } + + loaded_options *configuration::lookup_loaded_options(const string& target) { + // we assume 'target' is a directory with trailing slash appended + string scrc = root_source+target; + if(flags&flag_rc_file_name) + scrc += rc_file_name; + else + scrc += ".scrc"; + // TODO: normalize me, anyway. + if(access(scrc.c_str(),R_OK)) + return 0; // TODO FIXME: this approach leaves already loaded .scrcs around in case of removal + loaded_specs_t::iterator i = loaded_specs.find(target); + if(i==loaded_specs.end() || !i->second.is_valid()) { + if(i!=loaded_specs.end()) + loaded_specs.erase(i); + pair<loaded_specs_t::iterator,bool> ii = loaded_specs.insert(loaded_specs_t::value_type(target,loaded_options())); + assert(ii.first!=loaded_specs.end()); + ii.first->second.parse(this,scrc); + i = ii.first; + } + assert(i!=loaded_specs.end()); + return &(i->second); + } + + config_options::action_handler_t *config_options::lookup_action_handler(const string& target) { + for(action_handlers_t::iterator i=action_handlers.begin();i!=action_handlers.end();++i) { + if(i->regex.search(target)) + return &*i; + } + return NULL; + } + + string config_options::lookup_http_status_handler(const string& status) { + http_status_handlers_t::const_iterator i = http_status_handlers.find(status); + string rv; + if(i!=http_status_handlers.end()) + rv = i->second; + return rv; + } + + string configuration::lookup_http_status_handler(const string& target,const string& status) { + string t = "/"; + t += normalize_path(target,strip_leading_slash); + string rv; + for(;;) { + if(t[t.length()-1]=='/') { + loaded_options* lo = lookup_loaded_options(t); + if( lo && (lo->flags&config_options::flag_http_status_handlers) ) { + rv = lo->lookup_http_status_handler(status); + if(!rv.empty()) + return rv; + } + } + specs_t::iterator i = specs.find(t); + if( i!=specs.end() && (i->second.flags&&config_options::flag_http_status_handlers) ) { + rv = i->second.lookup_http_status_handler(status); + if(!rv.empty()) + return rv; + } + if(t.empty()) + return rv; + string::size_type sl=t.rfind('/'); + if(sl==string::npos) { + t.erase(); + }else{ + if(sl==(t.length()-1)) + t.erase(sl); + else + t.erase(sl+1); + } + } + } + + config_options::action_handler_t *configuration::lookup_action_handler(const string& target) { + string t = "/"; + t += normalize_path(target,strip_leading_slash); + for(;;) { + if(t[t.length()-1]=='/') { + loaded_options* lo = lookup_loaded_options(t); + if( lo && (lo->flags&config_options::flag_action_handlers) ) { + config_options::action_handler_t *rv = lo->lookup_action_handler(target); + if(rv) + return rv; + } + } + specs_t::iterator i = specs.find(t); + if( i!=specs.end() && (i->second.flags&&config_options::flag_action_handlers) ) { + config_options::action_handler_t *rv = i->second.lookup_action_handler(target); + if(rv) + return rv; + } + if(t.empty()) + return NULL; + string::size_type sl=t.rfind('/'); + if(sl==string::npos) { + t.erase(); + }else{ + if(sl==(t.length()-1)) + t.erase(sl); + else + t.erase(sl+1); + } + } + } + + config_options* configuration::lookup_config(const string& target,int flag) { + string t = "/"; // always assume leading slash + t += normalize_path(target,strip_leading_slash); + // XXX: reconsider precedence + for(;;) { + if(t[t.length()-1]=='/') { + loaded_options* lo = lookup_loaded_options(t); + if( lo && (lo->flags&flag)==flag ) + return lo; + } + specs_t::iterator i = specs.find(t); + if( i!=specs.end() && (i->second.flags&flag)==flag ) + return &(i->second); + if(t.empty()) + return NULL; + string::size_type sl=t.rfind('/'); + if(sl==string::npos) { + t.erase(); + }else{ + if(sl==(t.length()-1)) + t.erase(sl); + else + t.erase(sl+1); + } + } + } + + bool config_options::match_autobuild_files(const char *fn,bool &rv) { + for(list<string>::reverse_iterator i=auto_build_files.rbegin();i!=auto_build_files.rend();++i) { + const char *pat = i->c_str(); + bool plus = true; + if((*pat)=='+') + pat++; + else if((*pat)=='-') { + plus = false; + pat++; + } + if(!fnmatch(pat,fn,FNM_NOESCAPE|FNM_PATHNAME|FNM_PERIOD)) { + rv = plus; + return true; + } + } + return false; + } + + bool configuration::match_autobuild_files(const string& target,const char *fn) { + string t = "/"; + t += normalize_path(target,strip_leading_slash|strip_trailing_slash); + t += "/"; + bool rv = false; + for(;;) { + if(t[t.length()-1]=='/') { + loaded_options* lo = lookup_loaded_options(t); + if(lo && (lo->flags&config_options::flag_auto_build_files) && lo->match_autobuild_files(fn,rv) ) + return rv; + } + specs_t::iterator i = specs.find(t); + if( i!=specs.end() && (i->second.flags&config_options::flag_auto_build_files) && i->second.match_autobuild_files(fn,rv) ) + return rv; + if(t.empty()) + return rv; + string::size_type sl=t.rfind('/'); + if(sl==string::npos) { + t.erase(); + }else{ + if(sl==(t.length()-1)) + t.erase(sl); + else + t.erase(sl+1); + } + } + } + + void configuration::parse(const string& cfile) { + struct dc_context dcc; + dcc.cf = this; + dcc.ctx = DCC_ROOT; + dcc.co.push_front(&root_options()); + configfile_t *cf = dotconf_create((char*)cfile.c_str(),dc_options,(context_t*)&dcc,CASE_INSENSITIVE); + if(!cf) + throw konforka::exception(CODEPOINT,"failed to dotconf_create()"); + cf->errorhandler = (dotconf_errorhandler_t) dc_error_handler; + cf->contextchecker = (dotconf_contextchecker_t) dc_context_checker; + if(!dotconf_command_loop(cf)) + throw konforka::exception(CODEPOINT,"failed to dotconf_command_loop()"); + dotconf_cleanup(cf); + } + + void loaded_options::parse(configuration *config,const string& cfile) { + struct dc_context dcc; + dcc.cf = config; + dcc.ctx = DCC_SCRC; + dcc.co.push_front(this); + configfile_t *cf = dotconf_create((char*)cfile.c_str(),dc_options,(context_t*)&dcc,CASE_INSENSITIVE); + if(!cf) + throw konforka::exception(CODEPOINT,"failed to dotconf_create()"); + cf->errorhandler = (dotconf_errorhandler_t) dc_error_handler; + cf->contextchecker = (dotconf_contextchecker_t) dc_context_checker; + if(!dotconf_command_loop(cf)) + throw konforka::exception(CODEPOINT,"failed to dotconf_command_loop()"); + dotconf_cleanup(cf); + source_file = cfile; + stat(cfile.c_str(),&st); // TODO: handle errors? + } +} diff --git a/lib/file_factory.cc b/lib/file_factory.cc new file mode 100644 index 0000000..c6b5748 --- a/dev/null +++ b/lib/file_factory.cc @@ -0,0 +1,55 @@ +#ifdef USE_PCH + #include "pch.h" +#else + #include <sys/types.h> + #include <sys/stat.h> + #include <unistd.h> + #include <konforka/exception.h> + using namespace std; + #include "sitecing/file_factory.h" +#endif + +namespace sitecing { + + bool file_factory::is_uptodate(const string& dst,file_list_t* deps) { + file_list_t deplist; + file_list_t *fl = deps?deps:&deplist; + get_dependencies(dst,*fl); + struct stat stdst; + if(stat(dst.c_str(),&stdst)) + return false; + for(file_list_t::const_iterator i=fl->begin();i!=fl->end();i++) { + struct stat stdep; + if(stat(i->c_str(),&stdep)) + return false; + if(stdst.st_mtime<stdep.st_mtime) + return false; + if(!is_uptodate(*i)) + return false; + } + return true; + } + + void file_factory::make(const string& dst) { + try { + depth++; + if(depth>25) + throw konforka::exception(CODEPOINT,"recursed too deeply."); + file_list_t deps; + if(!is_uptodate(dst,&deps)) { + for(file_list_t::const_iterator i=deps.begin();i!=deps.end();i++) + make(*i); + build(dst); + } + depth--; + }catch(konforka::exception& ke) { + depth--; + ke.see(CODEPOINT); + throw; + }catch(...) { + depth--; + throw; + } + } + +} diff --git a/lib/pch.h b/lib/pch.h new file mode 100644 index 0000000..67f9d6d --- a/dev/null +++ b/lib/pch.h @@ -0,0 +1,45 @@ +#ifndef __PCH_H +#define __PCH_H + +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/wait.h> +#include <unistd.h> +#include <fcntl.h> +#include <sys/ipc.h> +#include <sys/shm.h> +#include <sys/sem.h> +#include <errno.h> +#include <dlfcn.h> +#include <fnmatch.h> + +#include <dotconf.h> + +#include <cstdarg> +#include <cassert> +#include <iostream> +#include <fstream> + +#include <vector> +using namespace std; + +#include <konforka/exception.h> + +#include "sitecing/acomponent.h" +#include "sitecing/cgi_component.h" +#include "sitecing/component_factory.h" +#include "sitecing/sitecing_util.h" +#include "sitecing/sitecing_exception.h" +#include "sitecing/component_so.h" +#include "sitecing/configuration.h" +#include "sitecing/file_factory.h" +#include "sitecing/sitecing_interface_cgi.h" +#include "sitecing/sitespace.h" +#include "sitecing/util.h" +#include "sitecing/scoreboard.h" +#include "sitecing/process_manager.h" + +#include "sitecing/sitecing_parser.h" +#include "sitecing/sitecing_enflesher.h" + +#endif /* __PCH_H */ diff --git a/lib/process_manager.cc b/lib/process_manager.cc new file mode 100644 index 0000000..48bcb03 --- a/dev/null +++ b/lib/process_manager.cc @@ -0,0 +1,152 @@ +#ifdef USE_PCH + #include "pch.h" +#else + #include <sys/types.h> + #include <unistd.h> + #include <sys/wait.h> + #include <signal.h> + #include <errno.h> + #include <cassert> + #include <string> + #include <konforka/exception.h> + using namespace std; + #include "sitecing/process_manager.h" +#endif + +namespace sitecing { + + process_manager::process_manager() + : min_children(1), max_children(MAX_SITECING_SCOREBOARD_SLOTS), + min_spare_children(0), max_spare_children(-1), finishing(false), + die_humbly(false) { + } + process_manager::~process_manager() { + if(die_humbly) + return; + for(int tmp=0;tmp<MAX_SITECING_SCOREBOARD_SLOTS;tmp++) { + scoreboard_slot *sslot = sboard.get_slot(tmp); + if((sslot->state!=scoreboard_slot::state_free) && (sslot->pid>0)) + kill(sslot->pid,SIGTERM); + } + collect_dead_souls(true); + } + + void process_manager::manage() { + while(!finishing) { + manage_children(); + // XXX: is it the way it should be? + sleep(10); + wait_for_children(); + } + collect_dead_souls(true); + } + + void process_manager::collect_dead_souls(bool actively) { + for(int tries=5;(tries>0) && (sboard.count_slots(scoreboard_slot::state_free)!=MAX_SITECING_SCOREBOARD_SLOTS);tries--) { + if(actively) { + for(int tmp=0;tmp<MAX_SITECING_SCOREBOARD_SLOTS;tmp++) { + scoreboard_slot *sslot = sboard.get_slot(tmp); + if((sslot->state!=scoreboard_slot::state_free) && (sslot->pid>0)) + kill(sslot->pid,SIGTERM); + } + } + wait_for_children(false); + // XXX: again.. is it the right way? + sleep(1); + } + } + + void process_manager::wait_for_children(bool hang) { + int status; + int o = WUNTRACED; + if(!hang) + o|=WNOHANG; + while(sboard.count_slots(scoreboard_slot::state_free)<MAX_SITECING_SCOREBOARD_SLOTS) { + pid_t pid = waitpid(-1,&status,o); + if(!pid) + return; + if(pid<0) { + if(errno==EINTR) + return; + throw konforka::exception(CODEPOINT,"failed to waitpid()"); + } + assert(pid); + int slot = sboard.get_slot_by_pid(pid); + sboard.free_slot(slot); + if(hang) + return; + } + } + + void process_manager::manage_children() { + if(!spawn_children()) + kill_children(); + else + sleep(1); // just to get some rest. + } + + bool process_manager::spawn_children() { + int total_children = 0; + int idle_children = 0; + for(int tmp=0;tmp<MAX_SITECING_SCOREBOARD_SLOTS;tmp++) { + switch(sboard.get_slot(tmp)->state) { + case scoreboard_slot::state_free: + break; + case scoreboard_slot::state_idle: + idle_children++; + default: + total_children++; + break; + } + } + int total_lack = 0; + if(total_children<min_children) + total_lack = min_children-total_children; + int idle_lack = 0; + if(idle_children<min_spare_children) + idle_lack = min_spare_children-idle_children; + bool rv = false; + for(;(idle_lack>0 || total_lack>0) && (total_children<max_children);idle_lack--,total_lack--,total_children++) { + spawn_child(); + rv = true; + } + return rv; + } + + bool process_manager::kill_children() { + int idle_children = 0; + for(int tmp=0;tmp<MAX_SITECING_SCOREBOARD_SLOTS;tmp++) { + if(sboard.get_slot(tmp)->state==scoreboard_slot::state_idle) + idle_children++; + } + int idle_excess = idle_children-max_spare_children; + bool rv = false; + for(int tmp=0;tmp<MAX_SITECING_SCOREBOARD_SLOTS && idle_excess>0;tmp++) { + scoreboard_slot *sslot = sboard.get_slot(tmp); + if((sslot->state==scoreboard_slot::state_idle) && (sslot->pid>0)) { + kill(sslot->pid,SIGTERM); + idle_excess--; + rv = true; + } + } + return rv; + } + + void process_manager::spawn_child() { + int slot = sboard.allocate_slot(); + pid_t pid = fork(); + if(pid<0) { + sboard.free_slot(slot); + throw konforka::exception(CODEPOINT,"failed to fork()"); + } + if(!pid) { + // child + sboard.get_slot(slot)->pid = getpid(); + process(slot); + _exit(0); + } + // parent + sboard.get_slot(slot)->pid = pid; + } + +} diff --git a/lib/scoreboard.cc b/lib/scoreboard.cc new file mode 100644 index 0000000..370cd93 --- a/dev/null +++ b/lib/scoreboard.cc @@ -0,0 +1,71 @@ +#ifdef USE_PCH + #include "pch.h" +#else + #include <sys/types.h> + #include <unistd.h> + #include <sys/ipc.h> + #include <sys/shm.h> + #include <cassert> + #include <string> + #include <konforka/exception.h> + using namespace std; + #include "sitecing/scoreboard.h" +#endif + +namespace sitecing { + + scoreboard::scoreboard() + : shmid(-1), slots(NULL) { + shmid = shmget(IPC_PRIVATE,MAX_SITECING_SCOREBOARD_SLOTS*sizeof(scoreboard_slot),IPC_CREAT|0600); + if(shmid<0) + throw konforka::exception(CODEPOINT,"failed to shmget()"); + slots = (scoreboard_slot*)shmat(shmid,NULL,0); + if(shmctl(shmid,IPC_RMID,NULL)) + throw konforka::exception(CODEPOINT,"failed to shmctl()"); + if(!slots) + throw konforka::exception(CODEPOINT,"failed to shmat()"); + for(int tmp=0;tmp<MAX_SITECING_SCOREBOARD_SLOTS;tmp++) + slots[tmp].state=scoreboard_slot::state_free; + } + scoreboard::~scoreboard() { + shmdt(slots); + } + + int scoreboard::allocate_slot() { + for(int tmp=0;tmp<MAX_SITECING_SCOREBOARD_SLOTS;tmp++) { + if(slots[tmp].state==scoreboard_slot::state_free) { + slots[tmp].state=scoreboard_slot::state_allocated; + slots[tmp].pid=0; + return tmp; + } + } + throw konforka::exception(CODEPOINT,"out of scoreboard slots"); + } + void scoreboard::free_slot(int slot) { + assert(slot>=0 && slot<MAX_SITECING_SCOREBOARD_SLOTS); + if(slots[slot].state==scoreboard_slot::state_free) + throw konforka::exception(CODEPOINT,"freeing unallocated slot"); + slots[slot].state=scoreboard_slot::state_free; + } + + scoreboard_slot *scoreboard::get_slot(int slot) { + assert(slot>=0 && slot<MAX_SITECING_SCOREBOARD_SLOTS); + return &slots[slot]; + } + int scoreboard::get_slot_by_pid(pid_t pid) { + for(int rv=0;rv<MAX_SITECING_SCOREBOARD_SLOTS;rv++) + if( (slots[rv].state!=scoreboard_slot::state_free) && (slots[rv].pid == pid) ) + return rv; + throw konforka::exception(CODEPOINT,"no such process"); + } + + int scoreboard::count_slots(enum scoreboard_slot::_state state) { + int rv = 0; + for(int tmp=0;tmp<MAX_SITECING_SCOREBOARD_SLOTS;tmp++) { + if(slots[tmp].state==state) + rv++; + } + return rv; + } + +} diff --git a/lib/sitecing_enflesher.ll b/lib/sitecing_enflesher.ll new file mode 100644 index 0000000..5f631d7 --- a/dev/null +++ b/lib/sitecing_enflesher.ll @@ -0,0 +1,202 @@ +%{ +#include <iostream> +#include <fstream> +#include <cassert> +#include <stdexcept> +using namespace std; +#include "sitecing/sitecing_exception.h" +using namespace sitecing; +#define sitecing_enflesher_flexlexer_once +#include "sitecing/sitecing_enflesher.h" +#include "sitecing/sitecing_parser.h" +#undef yyFlexLexer +#define yyFlexLexer sitecing_enflesherFlexLexer +%} +%option 8bit c++ verbose noyywrap yyclass="sitecing_enflesher" yylineno prefix="sitecing_enflesher" stack debug + +ID [A-Za-z_][A-Za-z0-9_]* + +%% + +^\%\%\#[^\n]+\n { + string line = yytext; + line.erase(0,3); + line.erase(line.length()-1); + outs.flush(); + outs.close(); + outs.clear(); + outs.open((parser.output_basename+line).c_str(),ios::trunc); + if(!outs.good()) + throw preprocessor_error(CODEPOINT,"failed to write preprocessor output"); + anchor(); + anchoraged = true; +} +^\%\%[^\n]+\n { + string line = yytext; + line.erase(0,2); + line.erase(line.length()-1); + outs.flush(); + outs.close(); + outs.clear(); + outs.open((parser.output_basename+line).c_str(),ios::trunc); + if(!outs.good()) + throw preprocessor_error(CODEPOINT,"failed to write preprocessor output"); + anchoraged = false; +} + +\<\%component_basename\%\> outs << parser.component_basename; anchor_time = true; +\<\%impl\%\> outs << parser.impl; anchor_time = true; +\<\%member_functions:impl\%\> { + for(sitecing_parser::member_functions_t::const_iterator i=parser.member_functions.begin();i!=parser.member_functions.end();i++) { + outs << i->type << " " << parser.class_name << "::"; + if(i->name.empty()) { + outs << parser.class_name << "()"; + bool first = true; + for(sitecing_parser::member_variables_t::iterator i=parser.member_variables.begin();i!=parser.member_variables.end();i++) { + if(i->initializer.empty()) + continue; + if(first) { + outs << ":"; + first=false; + }else{ + outs << ","; + } + if(i->bComponent) { + outs << i->name << "(NULL)"; + }else { + outs << i->name << "(" << i->initializer << ")"; + } + } + }else if(i->name == "~") + outs << "~" << parser.class_name << "()"; + else + outs << i->name << i->args; + outs << "{\n" << i->body << "\n}\n"; + } + anchor_time = true; +} +\<\%class_name\%\> outs << parser.class_name; anchor_time = true; +\<\%baseclass_header\%\> outs << parser.base_header; anchor_time = true; +\<\%decl\%\> outs << parser.decl; anchor_time = true; +\<\%baseclass_name\%\> outs << parser.base_class; anchor_time = true; +\<\%member_variables:decl\%\> { + for(sitecing_parser::member_variables_t::iterator i=parser.member_variables.begin();i!=parser.member_variables.end();i++) { + if(i->bComponent) { + if(i->type.empty()) { + i->type = parser.factory.get_classname(i->initializer); + } + if(i->bTypeOnly) { + outs << "typedef " << i->type << " " << i->name << ";\n"; + }else{ + outs << "typedef " << i->type << " __type_" << i->name << ";\nsitecing::so_component __soc_" << i->name << ";\n__type_" << i->name << " *" << i->name << ";\n"; + } + }else{ + outs << i->type << " " << i->name << ";\n"; + } + } + anchor_time = true; +} +\<\%member_functions:decl\%\> { + for(sitecing_parser::member_functions_t::const_iterator i=parser.member_functions.begin();i!=parser.member_functions.end();i++) { + (i->name.empty()?outs:outs << "virtual ") + << i->type << " "; + if(i->name.empty()) { + outs << parser.class_name << "()"; + }else if(i->name == "~") + outs << "~" << parser.class_name << "()"; + else + outs << i->name << i->args; + outs << ";\n"; + } + anchor_time = true; +} +\<\%imports:list\%\> { + for(sitecing_parser::member_variables_t::const_iterator i=parser.member_variables.begin();i!=parser.member_variables.end();i++) { + if(i->bComponent) + outs << i->initializer << endl; + } + anchor_time = true; +} +\<\%imports:includes\%\> { + for(sitecing_parser::member_variables_t::iterator i=parser.member_variables.begin();i!=parser.member_variables.end();i++) { + if(i->bComponent) + outs << "\n#include \"" << i->initializer << ".h\"\n"; + } + anchor_time = true; +} +\<\%imports:import\%\> { + for(sitecing_parser::member_variables_t::iterator i=parser.member_variables.begin();i!=parser.member_variables.end();i++) { + if(!i->bComponent) + continue; + if(i->bTypeOnly) + continue; + outs << "__soc_" << i->name << "=__SCIF->ss->fetch(\"" << i->initializer << "\",__SCIF); " << i->name << "=static_cast<__type_" << i->name << "*>(__soc_" << i->name << ".ac->__the_most_derived_this());\n"; + } + anchor_time = true; +} + +\<\%base_component\%\> { + // TODO: + anchor_time = true; +} + +\<\%ancestors:includes\%\> { + for(sitecing_parser::ancestor_classes_t::const_iterator i=parser.ancestor_classes.begin();i!=parser.ancestor_classes.end();++i) { + outs << "#include \"" << i->path << ".h\"\n"; + } + anchor_time = true; +} +\<\%ancestors:component_list\%\> { + for(sitecing_parser::ancestor_classes_t::const_iterator i=parser.ancestor_classes.begin();i!=parser.ancestor_classes.end();++i) { + outs << i->path << "\n"; + } + anchor_time = true; +} +\<\%ancestors:base_clause_part\%\> { + for(sitecing_parser::ancestor_classes_t::const_iterator i=parser.ancestor_classes.begin();i!=parser.ancestor_classes.end();++i) { + outs << ", virtual public " << parser.factory.get_classname(i->path); + } +} +\<\%ancestors:typedefs\%\> { + for(sitecing_parser::ancestor_classes_t::const_iterator i=parser.ancestor_classes.begin();i!=parser.ancestor_classes.end();++i) { + outs << "typedef class " << parser.factory.get_classname(i->path) << " " << i->name << ";\n"; + } + anchor_time = true; +} +\<\%ancestors:import\%\> { + for(sitecing_parser::ancestor_classes_t::const_iterator i=parser.ancestor_classes.begin();i!=parser.ancestor_classes.end();++i) { + outs << i->name << "::__do_imports();\n"; + } + anchor_time = true; +} + +\n { + if(anchor_time) + anchor(); + ECHO; +} +. ECHO; + +%% + +void sitecing_enflesher::LexerOutput(const char *buf,int size) { + outs.write(buf,size); +} + +void sitecing_enflesher::enflesh() { + ifstream ifs(parser.skeleton.c_str()); + if(!ifs.good()) + throw preprocessor_error(CODEPOINT,"failed to open skeleton file"); + switch_streams(&ifs,NULL); + yylex(); +} + +void sitecing_enflesher::anchor() { + if(!anchoraged) + return; + outs << "\n#line " << lineno() << " \"" << parser.skeleton << "\"\n"; + anchor_time = false; +} +/* + * vim:set ft=lex: + */ diff --git a/lib/sitecing_interface_cgi.cc b/lib/sitecing_interface_cgi.cc new file mode 100644 index 0000000..5c3d295 --- a/dev/null +++ b/lib/sitecing_interface_cgi.cc @@ -0,0 +1,25 @@ +#include <cassert> +#include "sitecing/sitecing_interface_cgi.h" + +namespace sitecing { + + sitecing_interface_cgi::sitecing_interface_cgi(sitespace *s) + : sitecing_interface(&prebuffer), ss(s), cgigw(NULL) { + } + + void sitecing_interface_cgi::prepare(kingate::cgi_gateway *cg) { + cgigw = cg; + headers.clear(); + headers["Content-Type"] = "text/html"; + prebuffer.str(""); + } + + void sitecing_interface_cgi::flush() { + assert(cgigw); + for(headers_t::const_iterator i=headers.begin();i!=headers.end();i++) + cgigw->out() << i->first << ": " << i->second << "\n"; + (cgigw->out() << "\n").write(prebuffer.str().c_str(),prebuffer.tellp()); + cgigw->out().flush(); + } + +} diff --git a/lib/sitecing_parser.ll b/lib/sitecing_parser.ll new file mode 100644 index 0000000..6cb78f3 --- a/dev/null +++ b/lib/sitecing_parser.ll @@ -0,0 +1,594 @@ +%{ + /* + * XXX: I have a strong feeling that this parser should be completely rewritten. + */ +#include <iostream> +#include <fstream> +#include <cassert> +#include <stdexcept> +using namespace std; +#include "sitecing/sitecing_util.h" +#include "sitecing/sitecing_exception.h" +using namespace sitecing; +#define sitecing_parser_flexlexer_once +#include "sitecing/sitecing_parser.h" +#include "sitecing/sitecing_enflesher.h" +#undef yyFlexLexer +#define yyFlexLexer sitecing_parserFlexLexer +%} +%x SLASHSTAR_COMMENT SLASHSLASH_COMMENT STRING +%x CODELINE CLASSLINE DECLLINE IMPLLINE DECLBLOCK IMPLBLOCK VARLINE VARINIT +%x IMPORTLINE IMPORTCOMPONENT +%x IMPORTTYPELINE IMPORTTYPECOMPONENT +%x DERIVELINE DERIVECOMPONENT +%x CONSTRUCTOR DESTRUCTOR CODEMETHODLINE CODEMETHODARGS +%x CODEMETHODBLOCK INLINE METHODLINE METHODARGS METHODBLOCK CODEBLOCK OUTPUTBLOCK +%option 8bit c++ verbose noyywrap yyclass="sitecing_parser" prefix="sitecing_parser" stack yylineno + +WHITESPACE [ \t] +ID [A-Za-z_][A-Za-z0-9_]* +NOIDCHAR [^A-Za-z0-9_] + +%% + +<INITIAL>{ + ^\%\%class{WHITESPACE}+ { + // TODO: signal error if we already have class name acquired from source. + modi.push_front(modus_operandi(modus_operandi::flag_devour_comments|modus_operandi::flag_devour_whitespace)); + BEGIN(CLASSLINE); + } + ^\%\%decl{WHITESPACE}+ { + modi.push_front(modus_operandi(0)); + anchor(); + BEGIN(DECLLINE); + } + ^\%\%impl{WHITESPACE}+ { + modi.push_front(modus_operandi(0)); + anchor(); + BEGIN(IMPLLINE); + } + \<\%decl\> { + modi.push_front(modus_operandi(0)); + anchor(); + BEGIN(DECLBLOCK); + } + \<\%impl\> { + modi.push_front(modus_operandi(0)); + anchor(); + BEGIN(IMPLBLOCK); + } + ^\%\%var{WHITESPACE}+ { + modi.push_front(modus_operandi(modus_operandi::flag_devour_comments)); + anchor(); + BEGIN(VARLINE); + } + ^\%\%import{WHITESPACE}+ { + modi.push_front(modus_operandi(modus_operandi::flag_devour_comments)); + BEGIN(IMPORTLINE); + } + ^\%\%import_type{WHITESPACE}+ { + modi.push_front(modus_operandi(modus_operandi::flag_devour_comments)); + BEGIN(IMPORTTYPELINE); + } + ^\%\%derive{WHITESPACE}+ { + modi.push_front(modus_operandi(modus_operandi::flag_devour_comments)); + BEGIN(DERIVELINE); + } + \<\%constructor\> { + modi.push_front(modus_operandi()); + anchor(); + BEGIN(CONSTRUCTOR); + } + \<\%destructor\> { + modi.push_front(modus_operandi()); + anchor(); + BEGIN(DESTRUCTOR); + } + \<\%codemethod{WHITESPACE}+ { + modi.push_front(modus_operandi(modus_operandi::flag_devour_comments)); + anchor(); + BEGIN(CODEMETHODLINE); + } + \<\%method{WHITESPACE}+ { + modi.push_front(modus_operandi(modus_operandi::flag_devour_comments)); + anchor(); + BEGIN(METHODLINE); + } + <<EOF>> { + assert(modi.size()==1); + M().modify(modus_operandi::modus_preop); + LexerOutput(";",1); + return 0; + } +} +<<EOF>> throw preprocessor_error(CODEPOINT,"unexpected end of file",lineno()); + +<CODEBLOCK,CODEMETHODBLOCK>{ + "<%output>" { + anchor(); + yy_push_state(OUTPUTBLOCK); + } +} + +<METHODLINE>{ + {WHITESPACE}+ { + modus_operandi& m = modi.front(); + if(!m.output.empty()) { + if(!m._lastid.empty()) { + if(!m._type.empty()) m._type += ' '; + m._type += m._lastid; + } + m._lastid = m.output; + m.output.clear(); + } + } + \* { + modus_operandi& m = modi.front(); + ECHO; + if(!m._lastid.empty()) { + if(!m._type.empty()) m._type += ' '; + m._type += m._lastid; + } + m._lastid = m.output; + m.output.clear(); + } + \( { + modus_operandi& m = modi.front(); + if(m.output.empty()) { + m._name=m._lastid; + }else{ + if(!m._lastid.empty()) { // XXX: lastid, I believe should never be emtpy... + if(!m._type.empty()) m._type += ' '; + m._type += m._lastid; + } + m._name = m.output; + m.output.clear(); + } + ECHO; + BEGIN(METHODARGS); + } +} +<METHODARGS>{ + \%\> { + modus_operandi& m = modi.front(); + m._args = m.output; + m.output.clear(); + anchor(); + BEGIN(METHODBLOCK); + } +} + +<INITIAL,METHODBLOCK,OUTPUTBLOCK>{ + \<\%{WHITESPACE}+ { + M().modify(modus_operandi::modus_postop); + anchor(); + LexerOutput("(",1); + yy_push_state(INLINE); + } + ^\%{WHITESPACE} { + M().modify(modus_operandi::modus_code); + anchor(); + yy_push_state(CODELINE); + } + \<\%code\> { + M().modify(modus_operandi::modus_code); + anchor(); + yy_push_state(CODEBLOCK); + } + "</%output>" { + if(YY_START!=OUTPUTBLOCK) throw preprocessor_error(CODEPOINT,"unexpected tag",lineno()); + M().modify(modus_operandi::modus_code); + anchor(); + yy_pop_state(); + } +} + +<INLINE>\%\> LexerOutput(")",1); M().modus=modus_operandi::modus_preop; yy_pop_state(); +<CODELINE>\n yy_pop_state(); + +<CODEMETHODLINE>{ + {WHITESPACE}+ { + modus_operandi& m = modi.front(); + if(!m.output.empty()) { + if(!m._lastid.empty()) { + if(!m._type.empty()) m._type += ' '; + m._type += m._lastid; + } + m._lastid = m.output; + m.output.clear(); + } + } + \* { + modus_operandi& m = modi.front(); + ECHO; + if(!m._lastid.empty()) { + if(!m._type.empty()) m._type += ' '; + m._type += m._lastid; + } + m._lastid = m.output; + m.output.clear(); + } + \( { + modus_operandi& m = modi.front(); + if(m.output.empty()) { + m._name=m._lastid; + }else{ + if(!m._lastid.empty()) { // XXX: lastid, I believe should never be emtpy... + if(!m._type.empty()) m._type += ' '; + m._type += m._lastid; + } + m._name = m.output; + m.output.clear(); + } + ECHO; + BEGIN(CODEMETHODARGS); + } +} +<CODEMETHODARGS>{ + \%\> { + modus_operandi& m = modi.front(); + m._args = m.output; + m.output.clear(); + m.flags=0; + anchor(); + BEGIN(CODEMETHODBLOCK); + } +} + +<IMPORTLINE>{ + {WHITESPACE}+ { } + {ID} { + if(!modi.front()._name.empty()) + throw preprocessor_error(CODEPOINT,"syntax error",lineno()); + modi.front()._name = yytext; + } + \= { + modi.front().output.clear(); + BEGIN(IMPORTCOMPONENT); + } +} +<IMPORTCOMPONENT>{ + {WHITESPACE}+ { } + \n { + modus_operandi& m = M(); + string::size_type t = m.output.find_first_not_of(" \t"); + if(t!=string::npos) + m.output.erase(0,t); + t = m.output.find_last_not_of(" \t;"); + if(t!=string::npos) + m.output.erase(t+1); + if(m.output[0]=='"' && m.output[m.output.length()-1]=='"') { + m.output.erase(0,1); + m.output.erase(m.output.length()-1); + } + string c = combine_path(component_basename,m.output); + member_variables.push_back(member_variable(m._type,m._name,normalize_path(c,strip_leading_slash),true)); + modi.pop_front(); + BEGIN(INITIAL); + } +} + +<IMPORTTYPELINE>{ + {WHITESPACE}+ { } + {ID} { + if(!modi.front()._name.empty()) + throw preprocessor_error(CODEPOINT,"syntax error",lineno()); + modi.front()._name = yytext; + } + \= { + modi.front().output.clear(); + BEGIN(IMPORTTYPECOMPONENT); + } +} +<IMPORTTYPECOMPONENT>{ + {WHITESPACE}+ { } + \n { + modus_operandi& m = M(); + string::size_type t = m.output.find_first_not_of(" \t"); + if(t!=string::npos) + m.output.erase(0,t); + t = m.output.find_last_not_of(" \t;"); + if(t!=string::npos) + m.output.erase(t+1); + if(m.output[0]=='"' && m.output[m.output.length()-1]=='"') { + m.output.erase(0,1); + m.output.erase(m.output.length()-1); + } + string c = combine_path(component_basename,m.output); + member_variables.push_back(member_variable(m._type,m._name,normalize_path(c,strip_leading_slash),true,true)); + modi.pop_front(); + BEGIN(INITIAL); + } +} + +<DERIVELINE>{ + {WHITESPACE}+ { } + {ID} { + if(!modi.front()._name.empty()) + throw preprocessor_error(CODEPOINT,"syntax_error",lineno()); + modi.front()._name = yytext; + } + \= { + modi.front().output.clear(); + BEGIN(DERIVECOMPONENT); + } +} +<DERIVECOMPONENT>{ + {WHITESPACE}+ { } + \n { + modus_operandi& m = M(); + string::size_type t = m.output.find_first_not_of(" \t"); + if(t!=string::npos) + m.output.erase(0,t); + t = m.output.find_last_not_of(" \t;"); + if(t!=string::npos) + m.output.erase(t+1); + if(m.output[0]=='"' && m.output[m.output.length()-1]=='"') { + m.output.erase(0,1); + m.output.erase(m.output.length()-1); + } + string c = combine_path(component_basename,m.output); + ancestor_classes.push_back(ancestor_class(m._name,normalize_path(c,strip_leading_slash))); + modi.pop_front(); + BEGIN(INITIAL); + } +} + +<VARLINE>{ + {WHITESPACE}+ { + modus_operandi& m = modi.front(); + if(!m.output.empty()) { + if(!m._lastid.empty()) { + if(!m._type.empty()) m._type += ' '; + m._type += m._lastid; + } + m._lastid = m.output; + m.output.clear(); + } + } + \* { + modus_operandi& m = modi.front(); + ECHO; + if(!m._lastid.empty()) { + if(!m._type.empty()) m._type += ' '; + m._type += m._lastid; + } + m._lastid = m.output; + m.output.clear(); + } + \;|\n|\= { + modus_operandi& m = modi.front(); + if(m.output.empty()) { + m._name=m._lastid; + }else{ + if(!m._lastid.empty()) { // XXX: lastid should never be emtpy, I believe? + if(!m._type.empty()) m._type += ' '; + m._type += m._lastid; + } + m._name=m.output; + m.output.clear(); + } + BEGIN(VARINIT); + if(*yytext!='=') + unput('\n'); + } +} +<VARINIT>{ + \n { + modus_operandi& m = modi.front(); + string::size_type t = m.output.find_first_not_of(" \t"); + if(t!=string::npos) + m.output.erase(0,t); + t = m.output.find_last_not_of(" \t;"); + if(t!=string::npos) + m.output.erase(t+1); + member_variables.push_back(member_variable(m._type,m._name,m.output)); + if(!m.output.empty()) + have_initializers=true; + modi.pop_front(); + BEGIN(INITIAL); + } +} +<DECLLINE>\n { + ECHO; + decl += modi.front().output; + modi.pop_front(); + BEGIN(INITIAL); +} +<IMPLLINE>\n { + ECHO; + impl += modi.front().output; + modi.pop_front(); + BEGIN(INITIAL); +} +<CLASSLINE>\n { + class_name = modi.front().output; + modi.pop_front(); + BEGIN(INITIAL); +} +<CLASSLINE,DECLLINE,IMPLLINE,VARLINE,VARINIT,IMPORTLINE,IMPORTCOMPONENT,CODEMETHODLINE,CODEMETHODARGS,INLINE,METHODLINE,METHODARGS,DECLBLOCK,IMPLBLOCK,CONSTRUCTOR,DESTRUCTOR,CODEMETHODBLOCK,CODELINE,CODEBLOCK>{ + "/*" { + yy_push_state(SLASHSTAR_COMMENT); + if(!M().devour_comments()) { + ECHO; + } + } + "//" { + yy_push_state(SLASHSLASH_COMMENT); + if(!M().devour_comments()) { + ECHO; + } + } + \" { + yy_push_state(STRING); + ECHO; + } + \'\\.\' { + ECHO; + } +} + +<INITIAL,METHODBLOCK,OUTPUTBLOCK>{ + \" soft_anchor(); M().modify(modus_operandi::modus_text); LexerOutput("\\\"",2); + \n soft_anchor(); M().modify(modus_operandi::modus_text); LexerOutput("\\n",2); + \r soft_anchor(); M().modify(modus_operandi::modus_text); LexerOutput("\\r",2); + \t soft_anchor(); M().modify(modus_operandi::modus_text); LexerOutput("\\t",2); + \b soft_anchor(); M().modify(modus_operandi::modus_text); LexerOutput("\\b",2); + \a soft_anchor(); M().modify(modus_operandi::modus_text); LexerOutput("\\a",2); + . soft_anchor(); M().modify(modus_operandi::modus_text); ECHO; + {WHITESPACE}+ soft_anchor(); M().modify(modus_operandi::modus_text); ECHO; +} + +<DECLBLOCK,IMPLBLOCK,CONSTRUCTOR,DESTRUCTOR,CODEMETHODBLOCK,METHODBLOCK,CODEBLOCK>{ + \<\/\%decl\> { + if(YY_START!=DECLBLOCK) throw preprocessor_error(CODEPOINT,"tags mismatch",lineno()); + decl += modi.front().output; + modi.pop_front(); + BEGIN(INITIAL); + } + \<\/\%impl\> { + if(YY_START!=IMPLBLOCK) throw preprocessor_error(CODEPOINT,"tags mismatch",lineno()); + impl += modi.front().output; + modi.pop_front(); + BEGIN(INITIAL); + } + \<\/\%constructor\> { + if(YY_START!=CONSTRUCTOR) throw preprocessor_error(CODEPOINT,"tags mismatch",lineno()); + member_functions.push_back(member_function("","","",modi.front().output)); + have_constructor = true; + modi.pop_front(); + BEGIN(INITIAL); + } + \<\/\%destructor\> { + if(YY_START!=DESTRUCTOR) throw preprocessor_error(CODEPOINT,"tags mismatch",lineno()); + member_functions.push_back(member_function("","~","",modi.front().output)); + modi.pop_front(); + BEGIN(INITIAL); + } + \<\/\%codemethod\> { + if(YY_START!=CODEMETHODBLOCK) throw preprocessor_error(CODEPOINT,"tags mismatch",lineno()); + modus_operandi& m = modi.front(); + member_functions.push_back(member_function(m._type,m._name,m._args,m.output)); + modi.pop_front(); + BEGIN(INITIAL); + } + \<\/%method\> { + if(YY_START!=METHODBLOCK) throw preprocessor_error(CODEPOINT,"tags mismatch",lineno()); + modus_operandi& m = modi.front(); + m.modify(modus_operandi::modus_code); + member_functions.push_back(member_function(m._type,m._name,m._args,m.output)); + modi.pop_front(); + BEGIN(INITIAL); + } + \<\/%code\> { + if(YY_START!=CODEBLOCK) throw preprocessor_error(CODEPOINT,"tags mismatch",lineno()); + yy_pop_state(); + } + \n ECHO; +} + +<SLASHSTAR_COMMENT>{ + "*/" { + if(!M().devour_comments()) { + ECHO; + } + yy_pop_state(); + unput(' '); + } + \n { + if(!M().devour_comments()) { + ECHO; + } + } +} +<SLASHSLASH_COMMENT>{ + \n { + if(!M().devour_comments()) { + ECHO; + } + yy_pop_state(); + if(YY_START!=CODEBLOCK && YY_START!=CODEMETHODBLOCK && YY_START!=IMPLBLOCK && YY_START!=DECLBLOCK) + unput('\n'); + } +} +<SLASHSTAR_COMMENT,SLASHSLASH_COMMENT>. { + if(!M().devour_comments()) { + ECHO; + } +} +<STRING>{ + \\. ECHO; + \" ECHO; yy_pop_state(); + . ECHO; +} + +{WHITESPACE}+ { + if(!(M().flags&modus_operandi::flag_devour_whitespace)) { + ECHO; + } +} + +%% + +sitecing_parser::sitecing_parser(component_factory& f) + : factory(f), have_initializers(false), have_constructor(false), + base_class("sitecing::cgi_component"), + base_header("sitecing/cgi_component.h"), + skeleton(__SC_DEFAULT_SKELETON) { + } + +void sitecing_parser::preprocess(const string& in) { + ifstream ifs(in.c_str(),ios::in); + if(!ifs.good()) + throw preprocessor_error(CODEPOINT,"failed to open input file"); + input_file = in; + modi.push_front(modus_operandi(0)); + switch_streams(&ifs,NULL); + if(yylex()) + throw preprocessor_error(CODEPOINT,"unknown error"); + member_functions.push_back(member_function("void","main","(int _magic,va_list _args)",M().output)); + if(have_initializers && !have_constructor) + member_functions.push_back(member_function("","","","")); + sitecing_enflesher enflesher(*this); + enflesher.enflesh(); +} + +void sitecing_parser::LexerOutput(const char* buf,int size) { + assert(modi.size()); + M().output.append(buf,size); +} + +static const char *modus_transitions + [sitecing_parser::modus_operandi::modi] + [sitecing_parser::modus_operandi::modi] = { +// To: +// code preop postop text From: + { "", "(*(__SCIF->out))", "(*(__SCIF->out))<<", "(*(__SCIF->out))<<\"" }, // code + { ";", "", "<<", "<<\"" }, // preop + { NULL, NULL, "", "\"" }, // postop + { "\";", "\"", "\"<<", "" } // text +}; + +void sitecing_parser::modus_operandi::modify(modus_t m) { + const char * x = modus_transitions[modus][m]; + assert(x); + output += x; + modus = m; +} + +void sitecing_parser::soft_anchor() { + if(M().modus!=modus_operandi::modus_text) + anchor(); +} +void sitecing_parser::anchor() { + if(M().modus==modus_operandi::modus_text) + M().modify(modus_operandi::modus_preop); + M().output += "\n#line "; + char tmp[7]; + snprintf(tmp,sizeof(tmp),"%d",lineno()); + M().output += tmp; + M().output += " \""; + M().output += input_file; + M().output += "\"\n"; +} +/* vim:set ft=lex: */ diff --git a/lib/sitecing_util.cc b/lib/sitecing_util.cc new file mode 100644 index 0000000..9b6c54e --- a/dev/null +++ b/lib/sitecing_util.cc @@ -0,0 +1,278 @@ +#ifdef USE_PCH + #include "pch.h" +#else + #include <sys/stat.h> + #include <sys/types.h> + #include <unistd.h> + #include <fcntl.h> + #include <sys/ipc.h> + #include <sys/sem.h> + #include <errno.h> + #include <iostream> + #include <fstream> + #include <cassert> + #include "sitecing/sitecing_util.h" +#endif + +namespace sitecing { + + /* + * XXX: all of these utilities could be sheerly optimized. + */ + + string normalize_path(const string& path,int opts) { + const char *s = path.c_str(); + string rv; + string::size_type notslash = 0; + if( (*s)=='.' && s[1]=='/' ) + s+=2; + if(opts&strip_leading_slash) + for(;(*s) && (*s)=='/';s++); + for(;*s;s++) { + if( (*s)=='/' ) { + if(s[1]=='/') + continue; + if(s[1]=='.' && s[2]=='/') { + s+=2; + continue; + } + } + if(opts&restrict_dotdot) { + if( + ( rv.empty() && s[0]=='.' && s[1]=='.' && s[2]=='/' ) // "^../" + || ( s[0]=='/' && s[1]=='.' && s[2]=='.' && (s[3]==0 || s[3]=='/') ) // "/..(/|$)" + ) + throw utility_restricted_sequence(CODEPOINT,"restricted updir sequence encountered"); + } + rv += *s; + if( (*s) != '/' ) + notslash=rv.length(); + } + if(!(opts&strip_trailing_slash)) + notslash++; + if(notslash<rv.length()) + rv.erase(notslash); // XXX: check the logic of stripping/not strippling trailing slash + return rv; + } + + string strip_prefix(const string& str,const string& prefix) { + if(str.compare(0,prefix.length(),prefix)) + throw utility_no_prefix(CODEPOINT,"no such prefix"); + return str.substr(prefix.length()); + } + + string strip_suffix(const string& str,const string& suffix) { + if(str.compare(str.length()-suffix.length(),suffix.length(),suffix)) + throw utility_no_suffix(CODEPOINT,"no such suffix"); + return str.substr(0,str.length()-suffix.length()); + } + + string dir_name(const string& filename) { + string::size_type sl = filename.find_last_of('/'); + if(sl==string::npos) + return ""; // no slashes -- no dir. + string::size_type nosl = filename.find_last_not_of('/',sl); + if(nosl==string::npos) + return ""; // only slashes -- no dir. XXX: only slashes after the last slash... does it mean no dir? + return filename.substr(0,nosl+1); + } + + void make_path(const string& path,mode_t mode) { + struct stat st; + for(string::size_type sl=0;sl!=string::npos;sl=path.find('/',sl+1)) { + if(!sl) + continue; + string p = path.substr(0,sl); + if(stat(p.c_str(),&st) || !S_ISDIR(st.st_mode)) { + if(mkdir(p.c_str(),mode)) + throw konforka::exception(CODEPOINT,"failed to mkdir()"); + } + } + if(stat(path.c_str(),&st) || !S_ISDIR(st.st_mode)) { + if(mkdir(path.c_str(),mode)) + throw konforka::exception(CODEPOINT,"failed to mkdir()"); + } + } + + void file_lock::lock(const string& f) { + unlock(); + fd = open(f.c_str(),O_CREAT|O_RDWR,S_IRUSR|S_IWUSR); + if(fd<0) + throw konforka::exception(CODEPOINT,"failed to open/create lockfile"); + try { + lock(); + }catch(konforka::exception& ke) { + ke.see(CODEPOINT); + close(fd); fd=-1; + throw; + }catch(...) { + close(fd); fd=-1; + throw; + } + } + void file_lock::lock() { + assert(fd>=0); + struct flock fl; + fl.l_type = F_WRLCK; + fl.l_whence=SEEK_SET; + fl.l_start=fl.l_len=0; + for(int tries=3;tries;tries--) { + if(!fcntl(fd,F_SETLK,&fl)) + return; + sleep(8); + } + throw konforka::exception(CODEPOINT,"failed to obtain file lock"); + } + void file_lock::unlock() { + if(fd<0) + return; + struct flock fl; + fl.l_type = F_UNLCK; + fl.l_whence=SEEK_SET; + fl.l_start=fl.l_len=0; + int rv = fcntl(fd,F_SETLK,&fl); + close(fd); + fd=-1; + if(rv) + throw konforka::exception(CODEPOINT,"failed to release file lock"); + } + + void pid_file::set(const string& f,bool u) { + ofstream of(f.c_str(),ios::trunc); + if(!of) + throw konforka::exception(CODEPOINT,"failed to open file for writing pid"); + of << getpid() << endl; + of.close(); + file_name = f; + unlink_pid = u; + } + void pid_file::unlink() { + if(!unlink_pid) + return; + ::unlink(file_name.c_str()); + } + + void semaphore::init() { + deinit(); + semid = semget(IPC_PRIVATE,1,IPC_CREAT|0600); + if(semid<0) + throw konforka::exception(CODEPOINT,"failed to semget()"); + if(semctl(semid,0,SETVAL,1)) + throw konforka::exception(CODEPOINT,"failed to semctl()"); + } + void semaphore::deinit() { + if(semid<0) + return; + semctl(semid,0,IPC_RMID,0); + } + void semaphore::on() { + assert(semid>=0); + struct sembuf sb; + sb.sem_num=0; + sb.sem_op=-1; + sb.sem_flg = SEM_UNDO; + while(semop(semid,&sb,1)<0) { + if(errno!=EINTR) + throw konforka::exception(CODEPOINT,"failed to semop()"); + } + } + void semaphore::off() { + assert(semid>=0); + struct sembuf sb; + sb.sem_num=0; + sb.sem_op=1; + sb.sem_flg = SEM_UNDO; + while(semop(semid,&sb,1)<0) { + if(errno!=EINTR) + throw konforka::exception(CODEPOINT,"failed to semop()"); + } + } + + void semaphore_lock::lock() { + assert(sem); + if(locked) + return; + sem->on(); + locked = true; + } + void semaphore_lock::unlock() { + if(!sem) + return; + if(!locked) + return; + sem->off(); + locked=false; + } + + string combine_path(const string& origin,const string& relative,int opts) { + string r = normalize_path(relative,0); + string rv; + // XXX: what to do if relative is empty is a question, really. + if(r.empty()) { + return normalize_path( (opts&origin_is_file)?dir_name(origin):origin ,strip_leading_slash|restrict_dotdot|strip_trailing_slash); + }else{ + if(r[0]=='/') { + r.erase(0,1); + }else{ + rv = normalize_path((opts&origin_is_file)?dir_name(origin):origin,restrict_dotdot|strip_trailing_slash); + } + } + string::size_type lsl = rv.rfind('/'); + for(string::size_type sl=r.find('/');sl!=string::npos;sl=r.find('/')) { + assert(sl!=0); + if(sl==1 && r[0]=='.') { + // it's a "./" + r.erase(0,2); + }else if(sl==2 && r[0]=='.' && r[1]=='.') { + // we have a "../" + if(lsl==string::npos) { + if(rv.empty() && (opts&fail_beyond_root)) + throw utility_beyond_root(CODEPOINT,"went beyond root while combining path"); + rv.clear(); + }else{ + rv.erase(lsl); + lsl = rv.rfind('/'); + } + r.erase(0,3); + }else{ + // we have a "something/" + lsl = rv.length(); + rv += '/'; + rv += r.substr(0,sl); + r.erase(0,sl+1); + } + } + if(r.empty()) + return rv+'/'; + if(r.length()==2 && r[0]=='.' && r[0]=='.') { + if(lsl==string::npos) { + if(rv.empty() & (opts&fail_beyond_root)) + throw utility_beyond_root(CODEPOINT,"went beyond root while combining path"); + return "/"; + }else{ + rv.erase(lsl+1); + return rv; + } + } + rv += '/'; + rv += r; + return rv; + } + + void auto_chdir::pushdir(const string& td,bool ap) { + char *tmp = get_current_dir_name(); + assert(tmp); + saved_pwd = tmp; + free(tmp); + autopop=ap; + if(chdir(td.c_str())) + throw konforka::exception(CODEPOINT,"failed to chdir()"); + } + void auto_chdir::popdir() { + autopop=false; + if(chdir(saved_pwd.c_str())) + throw konforka::exception(CODEPOINT,"failed to chdir()"); + // XXX: or should it be thrown? after all we call it from destructor... + } + +} diff --git a/lib/sitespace.cc b/lib/sitespace.cc new file mode 100644 index 0000000..0406d11 --- a/dev/null +++ b/lib/sitespace.cc @@ -0,0 +1,52 @@ +#ifdef USE_PCH + #include "pch.h" +#else + #include <cassert> + #include "sitecing/sitespace.h" + #include "sitecing/sitecing_util.h" +#endif + +namespace sitecing { + + sitespace::sitespace(configuration& c) + : config(c), factory(c) { } + + sitespace::~sitespace() { + for(sentenced_t::iterator i = sentenced.begin();i!=sentenced.end();++i) { + assert((*i)->chickens_used.empty()); + delete *i; + } + } + + so_component sitespace::fetch(const string& c,sitecing_interface* scif) { + execute_sentenced(); + string sobase = normalize_path(c); + string sopath = factory.root_so+sobase+".so"; + config_options *co_build = config.lookup_config(sobase,config_options::flag_build); + if( (!co_build) || co_build->build ) + factory.make(sopath); + components_t::iterator i = components.find(sopath); + if(i!=components.end()) { + if(i->second->is_uptodate()) + return so_component(i->second,scif); + if(i->second->chickens_used.empty()) { + delete i->second; + }else{ + sentenced.push_back(i->second); + } + components.erase(i); + } + pair<components_t::iterator,bool> ins = components.insert(components_t::value_type(sopath,new component_so(sopath))); + return so_component(ins.first->second,scif); + } + + void sitespace::execute_sentenced() { + for(sentenced_t::iterator i = sentenced.begin();i!=sentenced.end();++i) { + if((*i)->chickens_used.empty()) { + delete *i; + sentenced.erase(i); + } + } + } + +} diff --git a/lib/util.cc b/lib/util.cc new file mode 100644 index 0000000..1a81c56 --- a/dev/null +++ b/lib/util.cc @@ -0,0 +1,83 @@ +#ifdef USE_PCH + #include "pch.h" +#else + #include <cassert> + #include "sitecing/util.h" +#endif + +namespace sitecing { + + static const char *unsafeChars = "<>& \n\""; + + string html_escape(const string& str,int flags) { + string rv = str; + string::size_type screwed = 0; + for(;;) { + screwed = rv.find_first_of(unsafeChars,screwed); + if(screwed == string::npos) + break; + while(screwed<rv.length() && strchr(unsafeChars,rv.at(screwed))) { + char danger = rv.at(screwed); + switch(danger) { + case '<': + rv.replace(screwed,1,"<"); screwed+=4; + break; + case '>': + rv.replace(screwed,1,">"); screwed+=4; + break; + case '&': + rv.replace(screwed,1,"&"); screwed+=5; + break; + case ' ': + if(flags&html_escape_nbsp) { + rv.replace(screwed,1," "); screwed+=6; + }else + screwed++; + break; + case '\n': + if(flags&html_escape_br) { + if(flags&html_escape_br_noslash) { + rv.replace(screwed,1,"<br>\n"); screwed += 5; + }else{ + rv.replace(screwed,1,"<br/>\n"); screwed += 6; + } + }else + screwed++; + break; + case '\"': + if(flags&html_escape_quot) { + rv.replace(screwed,1,"""); screwed+=6; + }else + screwed++; + break; + default: + assert(false); + break; + } + } + } + return rv; + } + + void checkpoint::set() { + point = stream->tellp(); + if(last_will==will_intestate) + last_will = will_rollback; + } + + void checkpoint::make_will(will_t lw) { + last_will = lw; + } + + void checkpoint::rollback() { + stream->seekp(point); + if(last_will == will_rollback) + last_will = will_intestate; + } + void checkpoint::commit() { + point = stream->tellp(); + if(last_will == will_rollback) + last_will = will_intestate; + } + +} diff --git a/share/.gitignore b/share/.gitignore new file mode 100644 index 0000000..3dda729 --- a/dev/null +++ b/share/.gitignore @@ -0,0 +1,2 @@ +Makefile.in +Makefile diff --git a/share/Makefile.am b/share/Makefile.am new file mode 100644 index 0000000..7947380 --- a/dev/null +++ b/share/Makefile.am @@ -0,0 +1,3 @@ +pkgdata_DATA = component.skel + +EXTRA_DIST = component.skel diff --git a/share/component.skel b/share/component.skel new file mode 100644 index 0000000..f96c5b3 --- a/dev/null +++ b/share/component.skel @@ -0,0 +1,53 @@ +%%#.cc +#include "<%component_basename%>.h" +#undef __THIS_CLASS +#define __THIS_CLASS <%class_name%> +<%impl%> + +<%member_functions:impl%> + +void *<%class_name%>::__the_most_derived_this() { + return this; +} +void <%class_name%>::__do_imports() { + __base_class::__do_imports(); + <%ancestors:import%> + <%imports:import%> +} + +extern "C" sitecing::acomponent* _egg () { + return dynamic_cast<sitecing::acomponent*>(new <%class_name%>()); +} +%%#.h +#ifndef __<%class_name%>_H +#define __<%class_name%>_H +#include "<%baseclass_header%>" +<%ancestors:includes%> +<%imports:includes%> +#undef __THIS_CLASS +#define __THIS_CLASS <%class_name%> +<%decl%> + +class <%class_name%> : virtual public <%baseclass_name%><%ancestors:base_clause_part%> { + public: + typedef <%baseclass_name%> __base_class; + typedef <%class_name%> __this_class; + <%ancestors:typedefs%> + <%member_variables:decl%> + + <%member_functions:decl%> + + virtual void *__the_most_derived_this(); + virtual void __do_imports(); +}; + +#undef __THIS_CLASS +#endif /* __<%class_name%>_H */ +%%.imports +<%imports:list%> +%%.classname +<%class_name%> +%%.basecomponent +<%base_component%> +%%.ancestors +<%ancestors:component_list%> diff --git a/sitecing.pc.in b/sitecing.pc.in new file mode 100644 index 0000000..5801b51 --- a/dev/null +++ b/sitecing.pc.in @@ -0,0 +1,11 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +bindir=@bindir@ +libdir=@libdir@ +includedir=@includedir@ + +Name: sitecing +Description: site-C-ing web site development engine +Version: @VERSION@ +Requires: kingate konforka +Cflags: -I${includedir} diff --git a/src/.gitignore b/src/.gitignore new file mode 100644 index 0000000..6c2c79e --- a/dev/null +++ b/src/.gitignore @@ -0,0 +1,9 @@ +Makefile.in +sitecing-build +sitecing-fastcgi +COPYING.o +.libs +.deps +COPYING.cc +Makefile +*.o diff --git a/src/Makefile.am b/src/Makefile.am new file mode 100644 index 0000000..cc33f3f --- a/dev/null +++ b/src/Makefile.am @@ -0,0 +1,20 @@ +bin_PROGRAMS = sitecing-fastcgi sitecing-build + +INCLUDES = -I${top_srcdir}/include ${KINGATE_CFLAGS} ${DOTCONF_CFLAGS} \ + ${PCREPP_CFLAGS} +LIBS += ${top_builddir}/lib/libsitecing.la ${KINGATE_LIBS} ${DOTCONF_LIBS} \ + ${PCREPP_LIBS} + +sitecing_fastcgi_SOURCES = sitecing-fastcgi.cc \ + COPYING.cc +sitecing_fastcgi_LDFLAGS = -rdynamic +sitecing_fastcgi_DEPENDENCIES = ${top_builddir}/lib/libsitecing.la + +sitecing_build_SOURCES = sitecing-build.cc \ + COPYING.cc +sitecing_build_DEPENDENCIES = ${top_builddir}/lib/libsitecing.la + +COPYING.cc: ${top_srcdir}/COPYING + echo "const char * COPYING =" >$@ || (rm $@;exit 1) + sed 's/"/\\"/g' $< | sed 's/^/\"/' | sed 's/$$/\\n\"/' >>$@ || (rm $@;exit 1) + echo ";" >>$@ || (rm $@;exit 1) diff --git a/src/sitecing-build.cc b/src/sitecing-build.cc new file mode 100644 index 0000000..4cad0a3 --- a/dev/null +++ b/src/sitecing-build.cc @@ -0,0 +1,231 @@ +#include <sys/types.h> +#include <dirent.h> +#include <getopt.h> +#include <iostream> +#include <memory> +#include <fstream> +#include <cassert> +#include <set> +using namespace std; +#include "sitecing/sitecing_util.h" +#include "sitecing/util.h" +#include "sitecing/sitespace.h" +#include "sitecing/sitecing_interface_cgi.h" +#include "sitecing/cgi_component.h" +#include "sitecing/configuration.h" +#include "sitecing/magic.h" +#include "sitecing/sitecing_exception.h" +#include "sitecing/exception.h" +using namespace sitecing; + +#include "config.h" +#define PHEADER PACKAGE "-build Version " VERSION +#define PCOPY "Copyright (c) 2004 Klever Group" + +static sitespace* site_space = NULL; +typedef pair<dev_t,ino_t> the_inode_t; +set<the_inode_t> built_inodes; + +void build_component(const string& component) { + assert(site_space); + cerr << "Building " << component << endl; + try { + site_space->factory.make(site_space->config.root_so+component+".so"); + }catch(compile_error& ce) { + ce.see(CODEPOINT); + ifstream err((site_space->config.root_intermediate+ce.component_path+".stderr").c_str(),ios::in); + if(err) { + cerr << err.rdbuf(); + } + throw; + }catch(preprocessor_error& pe) { + pe.see(CODEPOINT); + cerr << site_space->config.root_source << pe.component_name << ":" << pe.line_number << ":" << pe.what() << endl; + throw; + } +} + +void build_imports(const string& component); + +void build_with_imports(const string& component) { + assert(site_space); + struct stat st; + string cp = site_space->config.root_source+component; + if(!lstat(cp.c_str(),&st)) { + if(built_inodes.find(the_inode_t(st.st_dev,st.st_ino))!=built_inodes.end()) + return; + built_inodes.insert(the_inode_t(st.st_dev,st.st_ino)); + } + build_component(component); + build_imports(component); +} + +void build_imports(const string& component) { + assert(site_space); + ifstream ifs((site_space->config.root_intermediate+component+".imports").c_str(),ios::in); + cerr << "Building components imported by " << component << endl; + if(ifs) { + string import; + while(!ifs.eof()) { + ifs >> import; + if(!import.empty()) + build_with_imports(import); + } + } +} + +void build_http_status_handlers(const string& target) { + assert(site_space); + set<string> stop_list; + string t = "/"; + t += normalize_path(target,strip_leading_slash); + for(;;) { + if(t[t.length()-1]=='/') { + loaded_options* lo = site_space->config.lookup_loaded_options(t); + if( lo && (lo->flags&config_options::flag_http_status_handlers) ) { + for(config_options::http_status_handlers_t::const_iterator i=lo->http_status_handlers.begin();i!=lo->http_status_handlers.end();++i) { + if(stop_list.find(i->first)==stop_list.end()) { + build_with_imports(i->second); + stop_list.insert(i->first); + } + } + } + } + configuration::specs_t::iterator i=site_space->config.specs.find(t); + if( i!=site_space->config.specs.end() && (i->second.flags&config_options::flag_http_status_handlers) ) { + for(config_options::http_status_handlers_t::const_iterator ii=i->second.http_status_handlers.begin();ii!=i->second.http_status_handlers.end();++i) { + if(stop_list.find(ii->first)==stop_list.end()) { + build_with_imports(ii->second); + stop_list.insert(ii->first); + } + } + } + if(t.empty()) + return; + string::size_type sl=t.rfind('/'); + if(sl==string::npos) { + t.erase(); + }else{ + if(sl==(t.length()-1)) + t.erase(sl); + else + t.erase(sl+1); + } + } +} + +void build_target(const string& target) { + assert(site_space); + string action = target; + config_options::action_handler_t *ah = site_space->config.lookup_action_handler(target); + if(ah) + action = ah->action; + build_with_imports(action); +} + +void build(const string& target) { + assert(site_space); + build_http_status_handlers(target); + config_options *co_exception_handler = site_space->config.lookup_config(target,config_options::flag_exception_handler); + if(co_exception_handler) { + string handler = co_exception_handler->exception_handler; + build_with_imports(handler); + } + string target_source = site_space->config.root_source+target; + struct stat st; + if(stat(target_source.c_str(),&st)) + throw konforka::exception(CODEPOINT,"failed to stat() target"); + if(S_ISREG(st.st_mode)) { + build_target(target); + }else if(S_ISDIR(st.st_mode)) { + build_http_status_handlers(target); + DIR *d=opendir(target_source.c_str()); + if(!d) + throw konforka::exception(CODEPOINT,"failed to opendir()"); + for(struct dirent *de=readdir(d);de;de=readdir(d)) { + if(!strcmp(de->d_name,".")) + continue; + if(!strcmp(de->d_name,"..")) + continue; + string subtarget = normalize_path(target+"/"+de->d_name); + struct stat sts; + if(stat((site_space->config.root_source+subtarget).c_str(),&sts)) + throw konforka::exception(CODEPOINT,"failed to stat() subtarget"); + if(S_ISDIR(sts.st_mode)) { + build(subtarget); + }else{ + if(site_space->config.match_autobuild_files(target,de->d_name)){ + build_target(subtarget); + } + } + } + closedir(d); + } +} + +int main(int argc,char **argv) { + try { + string config_file = "sitecing.conf"; + while(true) { + static struct option opts[] = { + { "help", no_argument, 0, 'h' }, + { "usage", no_argument, 0, 'h' }, + { "version", no_argument, 0, 'V' }, + { "license", no_argument, 0, 'L' }, + { "config", required_argument, 0, 'f' }, + { NULL, 0, 0, 0 } + }; + int c = getopt_long(argc,argv,"f:hVL",opts,NULL); + if(c==-1) + break; + switch(c) { + case 'h': + cerr << PHEADER << endl + << PCOPY << endl << endl + << " -h, --help" << endl + << " --usage display this text" << endl + << " -V, --version display version number" << endl + << " -L, --license show license" << endl + << " -f filename, --config=filename" << endl + << " specify configuration file to use" << endl; + exit(0); + break; + case 'V': + cerr << VERSION << endl; + exit(0); + break; + case 'L': + extern const char *COPYING; + cerr << COPYING << endl; + exit(0); + break; + case 'f': + config_file = optarg; + break; + default: + cerr << "Huh??" << endl; + break; + } + } + configuration config(config_file,true); + if(!(config.flags&configuration::flag_root_source)) + throw konforka::exception(CODEPOINT,"Unspecified root for sources"); + if(!(config.flags&configuration::flag_root_intermediate)) + throw konforka::exception(CODEPOINT,"Unspecified root for intermediate files"); + if(!(config.flags&configuration::flag_root_so)) + throw konforka::exception(CODEPOINT,"Unspecified root for shared objects"); + sitespace ss(config); + site_space = &ss; + if(optind<argc) { + for(int narg=optind;narg<argc;narg++) { + const char *arg = argv[narg]; + build(arg); + } + }else + build("/"); + return 0; + }catch(exception& e) { + cerr << "Oops: " << e.what() << endl; + return 1; + } +} diff --git a/src/sitecing-fastcgi.cc b/src/sitecing-fastcgi.cc new file mode 100644 index 0000000..963c257 --- a/dev/null +++ b/src/sitecing-fastcgi.cc @@ -0,0 +1,325 @@ +#include <sys/types.h> +#include <unistd.h> +#include <signal.h> +#include <getopt.h> +#include <pwd.h> +#include <grp.h> +#include <errno.h> +#include <syslog.h> +#include <iostream> +#include <memory> +#include <typeinfo> +using namespace std; +#include "kingate/fastcgi.h" +#include "kingate/cgi_gateway.h" +using namespace kingate; +#include "sitecing/sitecing_util.h" +#include "sitecing/util.h" +#include "sitecing/sitespace.h" +#include "sitecing/sitecing_interface_cgi.h" +#include "sitecing/cgi_component.h" +#include "sitecing/configuration.h" +#include "sitecing/magic.h" +#include "sitecing/sitecing_exception.h" +#include "sitecing/exception.h" +#include "sitecing/process_manager.h" +using namespace sitecing; + +#include "config.h" +#define PHEADER PACKAGE " Version " VERSION +#define PCOPY "Copyright (c) 2004 Klever Group" + +class cdummyClass : public acomponent { + public: + void main(int _magic,va_list _args) {} + void *__the_most_derived_this() { return NULL; } +} cdummyInstance; +class adummyClass : public cgi_component { + public: + void main(int _magic,va_list _args) {} + void *__the_most_derived_this() { return NULL; } +} adummyInstance; + +class sitecing_fastcgi_pm : public process_manager { + public: + configuration config; + fcgi_socket *fss; + semaphore sem; + bool multi; + uid_t uid; + gid_t gid; + pid_file pidfile; + + sitecing_fastcgi_pm(const string& config_file); + ~sitecing_fastcgi_pm(); + + void process(int slot); + + void run(); + void give_up_privs(); +}; + +sitecing_fastcgi_pm::sitecing_fastcgi_pm(const string& config_file) + : config(config_file), multi(false) { + if(( (config.flags&configuration::flag_user) || (config.flags&configuration::flag_group) || (config.flags&configuration::flag_chroot) ) && geteuid() ) + throw konforka::exception(CODEPOINT,"can't use User, Group or Chroot when started without root privileges"); + if(config.flags&configuration::flag_user) { + struct passwd *ptmp = getpwnam(config.user.c_str()); + if(ptmp) { + uid = ptmp->pw_uid; + }else{ + errno=0; + uid = strtol(config.user.c_str(),NULL,0); + if(errno) + throw konforka::exception(CODEPOINT,"failed to resolve User value to uid"); + } + } + if(config.flags&configuration::flag_group) { + struct group *gtmp = getgrnam(config.group.c_str()); + if(gtmp) { + gid = gtmp->gr_gid; + }else{ + errno=0; + gid = strtol(config.group.c_str(),NULL,0); + if(errno) + throw konforka::exception(CODEPOINT,"failed to resolve Group value to gid"); + } + } + if(!(config.flags&configuration::flag_root_source)) + throw konforka::exception(CODEPOINT,"unspecified root for sources"); + if(!(config.flags&configuration::flag_root_intermediate)) + throw konforka::exception(CODEPOINT,"unspecified root for intermediate files"); + if(!(config.flags&configuration::flag_root_so)) + throw konforka::exception(CODEPOINT,"unspecified root for shared objects"); + if(config.flags&configuration::flag_min_children) + min_children = config.min_children; + if(config.flags&configuration::flag_max_children) + max_children = config.max_children; + if(config.flags&configuration::flag_min_spare_children) + min_spare_children = config.min_spare_children; + if(config.flags&configuration::flag_max_spare_children) + max_spare_children = config.max_spare_children; + if(max_children<min_spare_children) + throw konforka::exception(CODEPOINT,"inconsistent numbers of MaxChildren and MinSpareChildren"); + if(min_children>max_spare_children && max_spare_children>=0) + throw konforka::exception(CODEPOINT,"inconsistent numbers of MinChildren and MaxSpareChildren"); + if(config.flags&configuration::flag_multi_process) { + multi = config.multi_process; + }else{ + if(config.flags&configuration::flag_listen_socket) + multi = true; + else + multi = false; + } + fss = (config.flags&configuration::flag_listen_socket)?new fcgi_socket(config.listen_socket.c_str(),5):new fcgi_socket(0); + if(!fss) + throw konforka::exception(CODEPOINT,"failed to establish listening socket"); + if(config.flags&configuration::flag_daemonize && config.daemonize) { + pid_t pf = fork(); + if(pf<0) + throw konforka::exception(CODEPOINT,"failed to fork()"); + if(pf) { + die_humbly=true; + _exit(0); + } + } + if(config.flags&configuration::flag_pid_file) { + pidfile.set(config.pid_file); + } + if(multi) + sem.init(); + } +sitecing_fastcgi_pm::~sitecing_fastcgi_pm() { + if(fss) + delete fss; +} + +void sitecing_fastcgi_pm::process(int slot) { + signal(SIGINT,SIG_DFL); + signal(SIGABRT,SIG_DFL); + signal(SIGTERM,SIG_DFL); + give_up_privs(); + scoreboard_slot *sslot = sboard.get_slot(slot); + try { + sitespace ss(config); + fcgi_socket& fs = *fss; + sitecing_interface_cgi scif(&ss); + string component_path; + string action; + config_options::action_handler_t *action_handler; + int rpc = 0; + if(config.flags&configuration::flag_requests_per_child) + rpc = config.requests_per_child; + for(int req=0;(rpc<=0) || (req<rpc);rpc++) { + semaphore_lock sl; + if(multi) { + sslot->state = scoreboard_slot::state_idle; + sl.sem = &sem; + sl.lock(); + } + sslot->state = scoreboard_slot::state_accept; + fcgi_interface fi(fs); + sslot->state = scoreboard_slot::state_processing; + if(multi) + sl.unlock(); + cgi_gateway gw(fi); + scif.prepare(&gw); + try { + component_path = normalize_path(gw.get_meta("PATH_INFO"),strip_leading_slash|strip_trailing_slash); + string full_component_path; + while(true) { + full_component_path = config.root_source+'/'+component_path; + if(!access(full_component_path.c_str(),F_OK)) + break; + string::size_type sl = component_path.rfind('/'); + if(sl==string::npos) + throw konforka::exception(CODEPOINT,"can't find the target component"); + component_path.erase(sl); + } + action = component_path; + action_handler = config.lookup_action_handler(component_path); + if(action_handler) { + action = action_handler->action; + } + string pwd = dir_name(full_component_path); + if(chdir(pwd.c_str())) + throw konforka::exception(CODEPOINT,"failed to chdir() into document's directory"); + so_component soc = ss.fetch(action,&scif); + if(action_handler) { + soc.ac->run(__magic_action, + config.root_source.c_str(), config.root_intermediate.c_str(), config.root_so.c_str(), + &(action_handler->args) + ); + }else{ + soc.ac->main(0,NULL); + } + }catch(http_status& hs) { + scif.headers["Status"] = hs.status+" "+hs.message; + string hshp = config.lookup_http_status_handler(component_path,hs.status); + if(!hshp.empty()) { + so_component hsh = ss.fetch(hshp,&scif); // TODO: handle error trying to handle status + hsh.ac->run(__magic_http_status,config.root_source.c_str(),config.root_intermediate.c_str(), + config.root_so.c_str(),action.c_str(),&hs); + } + }catch(compile_error& ce) { + config_options *co_exception_handler = config.lookup_config(component_path,config_options::flag_exception_handler); + if(co_exception_handler) { + so_component eh = ss.fetch(co_exception_handler->exception_handler,&scif); // TODO: handle error trying to handle error. + eh.ac->run(__magic_compile_error,ce.what(),config.root_source.c_str(),config.root_intermediate.c_str(),config.root_so.c_str(),ce.component_path.c_str()); + }else{ + ce.see(CODEPOINT); + throw; + } + }catch(preprocessor_error& pe) { + config_options *co_exception_handler = config.lookup_config(component_path,config_options::flag_exception_handler); + if(co_exception_handler) { + so_component eh = ss.fetch(co_exception_handler->exception_handler,&scif); // TODO: handle error trying to handle error. + eh.ac->run(__magic_preprocess_error,pe.what(),config.root_source.c_str(),config.root_intermediate.c_str(),config.root_so.c_str(),pe.component_name.c_str(),pe.line_number); + }else{ + pe.see(CODEPOINT); + throw; + } + }catch(exception& e) { + config_options *co_exception_handler = config.lookup_config(component_path,config_options::flag_exception_handler); + if(co_exception_handler) { + so_component eh = ss.fetch(co_exception_handler->exception_handler,&scif); // TODO: handle error trying to handle error. + eh.ac->run(__magic_generic_exception,e.what(),config.root_source.c_str(),config.root_intermediate.c_str(),config.root_so.c_str(),component_path.c_str(),&e); + } + } + scif.flush(); + } + }catch(exception& e) { + cerr << "->Oops: " << e.what() << endl; + } +} + +void sitecing_fastcgi_pm::run() { + if(multi) + manage(); + else + process(0); +} + +void sitecing_fastcgi_pm::give_up_privs() { + if(config.flags&configuration::flag_chroot) { + if(chroot(config.chroot.c_str())) + throw konforka::exception(CODEPOINT,"failed to chroot()"); + } + if(config.flags&configuration::flag_group) { + if((getgid()!=gid) && setgid(gid)) + throw konforka::exception(CODEPOINT,"failed to setgid()"); + } + if(config.flags&configuration::flag_user) { + if((getuid()!=uid) && setuid(uid)) + throw konforka::exception(CODEPOINT,"failed to setuid()"); + } +} + +static sitecing_fastcgi_pm* _process_manager = NULL; + +static void lethal_signal_handler(int signum) { + _process_manager->finishing=true; +} + +int main(int argc,char **argv) { + const char* id = *argv; + const char* t; + while(t = index(id,'/')) { + id=t; id++; + } + openlog(id,LOG_PERROR|LOG_PID,LOG_USER); + try { + string config_file = "sitecing.conf"; + while(true) { + static struct option opts[] = { + { "help", no_argument, 0, 'h' }, + { "usage", no_argument, 0, 'h' }, + { "version", no_argument, 0, 'V' }, + { "license", no_argument, 0, 'L' }, + { "config", required_argument, 0, 'f' }, + { NULL, 0, 0, 0 } + }; + int c = getopt_long(argc,argv,"f:hVL",opts,NULL); + if(c==-1) + break; + switch(c) { + case 'h': + cerr << PHEADER << endl + << PCOPY << endl << endl + << " -h, --help" << endl + << " --usage display this text" << endl + << " -V, --version display version number" << endl + << " -L, --license show license" << endl + << " -f filename, --config=filename" << endl + << " specify configuration file to use" << endl; + exit(0); + break; + case 'V': + cerr << VERSION << endl; + exit(0); + break; + case 'L': + extern const char *COPYING; + cerr << COPYING << endl; + exit(0); + break; + case 'f': + config_file = optarg; + break; + default: + cerr << "Huh??" << endl; + break; + } + } + sitecing_fastcgi_pm sfpm(config_file); + _process_manager = &sfpm; + signal(SIGINT,lethal_signal_handler); + signal(SIGABRT,lethal_signal_handler); + signal(SIGTERM,lethal_signal_handler); + sfpm.run(); + }catch(exception& e) { + /* cerr << "Oops: " << e.what() << endl; */ + syslog(LOG_ERR,"uncaught exception: %s",e.what()); + } + closelog(); +} |