Browse Source

Merge remote-tracking branch 'duda/duda_light' into duda-946

Hamilton Turner 11 years ago
parent
commit
be2b41b215

+ 3 - 0
duda/.gitignore

@@ -0,0 +1,3 @@
+*.o
+*~
+

+ 27 - 0
duda/README.md

@@ -0,0 +1,27 @@
+# Duda I/O Benchmarking Test
+
+This is the web service used to benchmark Duda I/O web services framework.
+
+http://duda.io
+
+## Requirements
+
+Just the GNU C Compiler and a Linux system running Kernel version >= 2.6.32
+
+## Tests available
+
+### 1. JSON
+
+URL: /json
+
+### 6. Plain text
+
+URL: /plaintext
+
+## About pending tests
+
+Most of tests that are related to database query are pending and will be available for the next Round.
+
+## Contact
+
+Eduardo Silva <[email protected]>

+ 24 - 0
duda/benchmark_config

@@ -0,0 +1,24 @@
+{
+  "framework": "duda",
+  "tests": [{
+    "default": {
+      "setup_file": "setup",
+      "json_url": "/json",
+      "plaintext_url": "/plaintext",
+      "port": 2001,
+      "approach": "Realistic",
+      "classification": "Platform",
+      "database": "",
+      "framework": "Duda",
+      "language": "C",
+      "orm": "Raw",
+      "platform": "Duda I/O",
+      "webserver": "Monkey",
+      "os": "Linux",
+      "database_os": "Linux",
+      "display_name": "Duda I/O",
+      "notes": "",
+      "versus": ""
+    }
+  }]
+}

+ 5 - 0
duda/dudac/.gitignore

@@ -0,0 +1,5 @@
+*.pyc
+logs/
+*.egg-info
+dist/
+build/

+ 139 - 0
duda/dudac/ChangeLog

@@ -0,0 +1,139 @@
+commit 56ff395015f03af2e532c3229538099e61ccc69b
+Author: Eduardo Silva <[email protected]>
+Date:   Tue Sep 25 11:35:36 2012 -0600
+
+    Do not reprint version when -v argument is used
+    
+    Signed-off-by: Eduardo Silva <[email protected]>
+
+commit f44524973519ae9a604c9b0641226242a635596f
+Author: Eduardo Silva <[email protected]>
+Date:   Tue Sep 18 15:36:40 2012 -0600
+
+    Check compiler warning and print to STDOUT
+    
+    Signed-off-by: Eduardo Silva <[email protected]>
+
+commit 6965d056e9409f8a2af9509d0f717c17e0bb0eed
+Author: Eduardo Silva <[email protected]>
+Date:   Tue Sep 18 14:45:59 2012 -0600
+
+    Fix reset environment for Duda git
+    
+    Signed-off-by: Eduardo Silva <[email protected]>
+
+commit a8748ede3a4e2f9accecfdb48639a65b4e68ae74
+Author: Eduardo Silva <[email protected]>
+Date:   Mon Sep 17 23:11:15 2012 -0600
+
+    Update README with examples details
+    
+    Signed-off-by: Eduardo Silva <[email protected]>
+
+commit 60927352cc2ca93bc17196298815ec8370bb4641
+Author: Eduardo Silva <[email protected]>
+Date:   Sun Sep 16 09:32:44 2012 -0600
+
+    DudaC v0.4
+    
+     - Enable only logger and liana plugins
+     - If the webservice contains an 'html' directory, enable the
+       DocumentRoot key under WEB_SERVICE section
+     - Colorize some outputs
+    
+    Signed-off-by: Eduardo Silva <[email protected]>
+
+commit 9dd6758af465214ec4c5a03c6e7e95bb09dfd3cd
+Author: Eduardo Silva <[email protected]>
+Date:   Sat Jun 2 12:34:57 2012 -0600
+
+    New stage directory
+    
+    DudaC now support to clone Monkey and Duda sources from GIT. That
+    info is stored into ~/.dudac/ , as Duda lives in a different repository
+    we need a new place to merge both parts. A new directory named 'stage'
+    has been added under ~/.dudac/stage. Inside it when a build is requested,
+    DudaC will generate a 'git archive' from both sources and do the merge.
+    
+    Then all configuration and build process is done in the stage version.
+    
+    Signed-off-by: Eduardo Silva <[email protected]>
+
+commit 9aae526887969c4b29b58e3b97068d7b2c1974c4
+Author: Eduardo Silva <[email protected]>
+Date:   Fri Jun 1 11:03:51 2012 -0600
+
+    First work to support separated DudaC repository
+    
+    Signed-off-by: Eduardo Silva <[email protected]>
+
+commit 7fdb55ab7e3e37012e4d1fbb02f67599f6ae1269
+Author: Eduardo Silva <[email protected]>
+Date:   Wed May 2 20:33:54 2012 -0600
+
+    DudaC v0.2
+    
+     - New flag -f to avoid compile Monkey sources everytime that the
+       web service runs
+     - Flags -s and -g can be used together with -w
+     - Web services now can support a 'config.dudac' file, this one
+       specify the requirements for the web service in terms of Monkey
+       GIT head, GIT URL and SSH URL.
+    
+    Signed-off-by: Eduardo Silva <[email protected]>
+
+commit 2fbc2d47c672801028712701441bd36a99fec62f
+Author: Eduardo Silva <[email protected]>
+Date:   Tue May 1 14:58:02 2012 -0600
+
+    Fix pull command
+    
+    Signed-off-by: Eduardo Silva <[email protected]>
+
+commit c934286aec7a7bf569485d215b5b08476b7ed4e2
+Author: Eduardo Silva <[email protected]>
+Date:   Sun Apr 22 10:50:04 2012 -0600
+
+    Use new Duda headers path
+    
+    Signed-off-by: Eduardo Silva <[email protected]>
+
+commit ca557aa8831befb7021d816fe4e50c05632b6ad4
+Author: Eduardo Silva <[email protected]>
+Date:   Wed Mar 14 11:22:47 2012 -0600
+
+    dudac: add -p parameter to specify TCP port
+    
+    Signed-off-by: Eduardo Silva <[email protected]>
+
+commit a06a7ea70b323f88529a45a586cb36d949f2404a
+Author: Eduardo Silva <[email protected]>
+Date:   Tue Mar 13 00:37:22 2012 -0600
+
+    dudac: update banner
+    
+    Signed-off-by: Eduardo Silva <[email protected]>
+
+commit 81a35be0e8f238f2e05cba01af7fe293065c8b87
+Author: Eduardo Silva <[email protected]>
+Date:   Tue Mar 13 00:32:12 2012 -0600
+
+    dudac: safe directory
+    
+    Signed-off-by: Eduardo Silva <[email protected]>
+
+commit 11a15b9213ff49b871284276c747892016cd7731
+Author: Eduardo Silva <[email protected]>
+Date:   Tue Mar 13 00:16:32 2012 -0600
+
+    DudaC: Update README
+    
+    Signed-off-by: Eduardo Silva <[email protected]>
+
+commit ab49953106909e13f2156f416d8c4480a96a7461
+Author: Eduardo Silva <[email protected]>
+Date:   Tue Mar 13 00:14:37 2012 -0600
+
+    DudaC: Initial import
+    
+    Signed-off-by: Eduardo Silva <[email protected]>

+ 1 - 0
duda/dudac/MANIFEST.in

@@ -0,0 +1 @@
+include dudaclient/dudac.Make

+ 48 - 0
duda/dudac/README

@@ -0,0 +1,48 @@
+Duda Client Manager
+===================
+DudaC is a command line interface for the Duda I/O web services framework 'Duda',
+it aims to to manage Duda web services in a easy manner.
+
+Install
+=======
+
+1) Install ``dudac`` in you home:
+
+   $ python setup.py install --user
+
+2) Add ``$HOME/.local/bin`` to your ``$PATH`` adding the following line to
+   your ``.bashrc``:
+
+   export PATH="$HOME/.local/bin:$PATH"
+
+3) Open a new terminal or source ``.bashrc`` and you'll have available the
+   ``dudac`` command.
+
+Usage
+=====
+1) Get the development environment:
+
+   # dudac -s
+
+2) Launch your web service:
+
+   # dudac -w PATH_TO_WEBSERVICE_SOURCE_CODE
+
+3) Hit your web server
+
+   # firefox http://localhost:2001
+
+Examples
+========
+If you want to see some Duda web services examples clone the following
+repository:
+
+    git clone https://github.com/monkey/duda-examples.git
+
+Then you can do something like:
+
+   # dudac -w /path/to/duda-examples/001_hello_world
+
+Author
+======
+Eduardo Silva <[email protected]>

+ 9 - 0
duda/dudac/dudac

@@ -0,0 +1,9 @@
+#!/usr/bin/env python2
+import sys
+import os
+
+dot = os.path.dirname(os.path.abspath(__file__))
+sys.path.insert(0, dot)
+
+from dudaclient.main import main
+main()

+ 0 - 0
duda/dudac/dudaclient/__init__.py


+ 16 - 0
duda/dudac/dudaclient/dudac.Make

@@ -0,0 +1,16 @@
+_PATH     = $(patsubst /%, %, $(CURDIR))
+_CC       = @/bin/echo -e "  [\033[33mCC\033[0m]   $@"; $(CC)
+_DD       = @/bin/echo -e "  [\033[32mDD\033[0m]   $@"; $(CC)
+_CC_QUIET = @/bin/echo -n; $(CC)
+
+all: $(NAME).duda
+
+$(NAME).duda: $(OBJECTS)
+	$(_DD) $(CFLAGS) $(DEFS) -shared -o $@ $^ -lc $(LDFLAGS)
+
+.c.o:
+	$(_CC) -c $(CFLAGS) $(DEFS) $(INCDIR) -fPIC $<
+	$(_CC_QUIET) -MM -MP $(CFLAGS) $(DEFS) $(INCDIR) $*.c -o $*.d > /dev/null &2>&1
+
+clean:
+	rm -rf *.o *.d *~ $(NAME).duda

+ 163 - 0
duda/dudac/dudaclient/git.py

@@ -0,0 +1,163 @@
+# Copyright (C) 2012, Eduardo Silva <[email protected]>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+
+import os
+import sys
+import shutil
+import commands
+
+from utils import *
+
+PROTOCOL_HTTPS = 0
+PROTOCOL_GIT   = 1
+
+class GitProject(object):
+    # Project and repository details
+    project_name = None
+    version  = 'master'
+    https_repo = None      # 'https://github.com/monkey/monkey.git'
+    git_repo   = None      # '[email protected]:monkey/monkey.git'
+
+    # Internal usage variables
+    recent_update   = False
+    recent_master   = False
+    recent_snapshot = False
+
+    def __init__(self, project_name, https_repo, git_repo):
+        self.project_name = project_name
+        self.https_repo = https_repo
+        self.git_repo   = git_repo
+
+    def set_protocol(self, protocol):
+        if protocol == PROTOCOL_HTTPS:
+            self.protocol = self.https_repo
+        elif protocol == PROTOCOL_GIT:
+            self.protocol = self.git_repo
+
+    def setup(self, version, https_repo, git_repo):
+        if version is not None:
+            self.version = version
+
+        if https_repo is not None:
+            self.https_repo = https_repo
+
+        if git_repo is not None:
+            self.git_repo = git_repo
+
+    def clone(self, to):
+        cmd = "git clone " + self.protocol + " " + to
+        execute("%s: cloning source code" % (self.project_name), cmd)
+
+    def update(self, to):
+        if self.recent_update is True:
+            return
+
+        self.master()
+
+        cpath = os.getcwd()
+        os.chdir(to)
+        cmd = "git pull"
+        execute("GIT %s: updating" % (self.project_name), cmd)
+        os.chdir(cpath)
+
+        self.recent_update = True
+
+    def remove(self, path):
+        cmd = "rm -rf %s" % path
+        print "[+] Deleting %s code..." % (self.project_name),
+        try:
+                shutil.rmtree(path)
+                print "\t[OK]"
+        except:
+                print "\t[FAILED]"
+
+    def run(self, cmd):
+        return commands.getstatusoutput(cmd)
+
+    def print_line(self, l):
+        print l,
+        sys.stdout.flush()
+
+    # The home method must be implemented by the subclass
+    def home(self):
+        pass
+
+    # Return the current head hash
+    def master(self):
+        home = self.home()
+        if os.path.exists(home) is False:
+            pass
+
+        if self.recent_master is True:
+            return
+
+        cmd = 'git checkout master'
+        cpath = os.getcwd()
+        os.chdir(home)
+        execute("GIT %s: checkout master" % (self.project_name), cmd)
+        os.chdir(cpath)
+
+        self.recent_master = True
+
+    def archive_to(self, path):
+        cmd =  'git checkout-index -a -f --prefix=%s/' % (path)
+
+        if self.version:
+            v = self.version
+            if v.startswith('commit') or v.startswith('tag') or v.startswith('branch'):
+                arr = v.split('@')
+                cmd = 'git checkout %s' % (arr[1])
+
+        cpath = os.getcwd()
+        os.chdir(self.home())
+        execute('%-12s: archive' % ('GIT %s' % self.project_name), cmd)
+
+    def check_reference(self, ref):
+        cmd = 'git show-ref %s' % ref
+        ret = commands.getstatusoutput(cmd)
+        if os.WEXITSTATUS(ret[0]) == 0:
+            return True
+
+        return False
+
+    # A snapshot takes the value of the 'version' key and set the GIT repository
+    # to the specified point, a few examples:
+    #
+    #    version = master
+    #    version = commit@dc2d07d8ac991392aaf8de8ea82cc60257a15670
+    #    version = branch@my_devel_branch
+    #    version = [email protected]
+    #
+    def snapshot(self):
+        if self.recent_snapshot is True:
+            return
+
+        cmd = 'git checkout %s' % (self.version)
+        cpath = os.getcwd()
+        os.chdir(self.home())
+
+        # Validate GIT reference
+        ref = self.check_reference(self.version)
+        if ref is False:
+            fail_msg("Error: invalid API level, aborting.")
+            sys.exit(1)
+
+        cmd = 'git stash && git checkout master && %s' % (cmd)
+        ghead = 'GIT %s' % self.project_name
+        execute("%-12s: switch HEAD to '%s'" % (ghead, self.version), cmd)
+        os.chdir(cpath)
+
+        self.recent_snapshot = True

+ 873 - 0
duda/dudac/dudaclient/main.py

@@ -0,0 +1,873 @@
+#!/usr/bin/env python2
+
+# Copyright (C) 2012-2014, Eduardo Silva <[email protected]>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+
+import os
+import sys
+import shutil
+import getopt
+import datetime
+import ConfigParser
+
+from git import GitProject
+from utils import *
+
+# Version
+DUDAC_VERSION  = "0.23"
+
+# Internal / Protocol
+PROTOCOL_HTTPS = 0
+PROTOCOL_GIT   = 1
+
+class DudaConfig(ConfigParser.ConfigParser):
+    def __init__(self):
+        ConfigParser.ConfigParser.__init__(self)
+
+    def open(self, path):
+        self.read(path)
+
+    def get_handlers(self):
+        return self.sections()
+
+    def get_key(self, handler, key):
+        try:
+            val = self.get(handler, key)
+            return val
+        except:
+            return None
+
+class Monkey:
+    opts = ''
+    recent_configure = False
+    recent_build = False
+    recent_clean = False
+
+    def __init__(self, source_path):
+        self.mk_path = source_path
+        self.SSL = False
+
+    def configure(self):
+        if self.recent_configure is True:
+            return
+
+        cpath = os.getcwd()
+        os.chdir(self.mk_path)
+
+        configure = "./configure --debug --disable-plugins='*' --enable-plugins='liana,duda' " + self.opts
+
+        # If we have SSL enable, we have to replace the transport layer, that means
+        # disable Liana and enable the new PolarSSL, later we need to generate
+        # certificates and configure everything to make it work properly
+        if self.SSL is True:
+            configure = configure.replace('liana','polarssl')
+
+        # Run the configure script
+        execute("Monkey      : prepare build", configure)
+
+        # Revert to original path
+        os.chdir(cpath)
+        self.recent_configure = True
+
+    def make_build(self):
+        if self.recent_build is True:
+            return
+
+        cmd = "make -C %s" % (self.mk_path)
+        execute("Monkey      : building", cmd)
+
+        self.recent_build = True
+
+    def make_clean(self):
+        if self.recent_clean is True:
+            return
+
+        if os.path.exists(self.mk_path + '/Makefile') is False:
+            return
+
+        cmd = "make -C %s clean" % (self.mk_path)
+        execute("Monkey      : cleaning", cmd)
+
+        self.recent_clean = True
+
+    def system(self, cmd):
+        return commands.getstatusoutput(cmd)
+
+    def update_transport_layer(self, t='plain'):
+        if t == 'plain':
+            pass
+        elif t == 'SSL':
+            pass
+
+class MonkeyGIT (GitProject):
+    def __init__(self):
+        https_repo = 'https://github.com/monkey/monkey.git'
+        git_repo   = '[email protected]:monkey/monkey.git'
+        GitProject.__init__(self, 'Monkey', https_repo, git_repo)
+
+    def home(self):
+        # Check our user temporal directory
+        home = os.getenv('USERPROFILE') or os.getenv('HOME')
+        dudac_home = "%s/.dudac" % (home)
+
+        return "%s/monkey" % (dudac_home)
+
+class DudaGIT(GitProject):
+    def __init__(self):
+        https_repo = 'https://github.com/monkey/duda.git'
+        git_repo   = '[email protected]:monkey/duda.git'
+        GitProject.__init__(self, 'Duda', https_repo, git_repo)
+
+    def home(self):
+        # Check our user temporal directory
+        home = os.getenv('USERPROFILE') or os.getenv('HOME')
+        dudac_home = "%s/.dudac" % (home)
+
+        return "%s/duda" % (dudac_home)
+
+class Duda:
+    # Default configuration directives for Monkey configuration
+    # file located under the [SERVER] section
+    MCONF_KNOWN = ['Port',
+                   'Listen',
+                   'Workers',
+                   'Timeout',
+                   'PidFile',
+                   'UserDir',
+                   'Indexfile',
+                   'HideVersion',
+                   'Resume',
+                   'User',
+                   'KeepAlive',
+                   'KeepAliveTimeout',
+                   'MaxKeepAliveRequest',
+                   'MaxRequestSize',
+                   'SymLink',
+                   'TransportLayer',
+                   'DefaultMimeType']
+
+    def __init__(self):
+        self.print_version()
+        self.web_service = None
+        self.service = None
+        self.port = 2001
+        self.SSL = False
+        self.SSL_default = False
+        self.output_stdout = False
+        self.api_level = DEFAULT_API_LEVEL
+        self.linux_malloc = False
+        self.linux_trace = False
+        self.jemalloc_stats = False
+        self.jemalloc_prof  = False
+        self.rebuild_monkey = False
+        self.mk_git = MonkeyGIT()
+        self.duda_git = DudaGIT()
+        self.mk_home = self.mk_git.home()
+        self.duda_home  = self.duda_git.home()
+        self.dudac_wd   = '%s/.dudac' % (os.getenv('USERPROFILE') or os.getenv('HOME'))
+        self.dudac_stage_path = os.getenv('DUDAC_STAGE_PATH')
+        self.load_makefile()
+
+        # In case the DUDAC_STAGE_PATH environment variable is set, use
+        # the alternative stage path. This is useful when using a fixed-path
+        # version of Monkey and Duda plugin, mostly for development purposes
+        if self.dudac_stage_path is not None:
+            self.stage_home = self.dudac_stage_path
+        else:
+            self.stage_home = self.dudac_wd + '/stage/'
+
+        self.monkey  = Monkey(self.stage_home + 'monkey/')
+        self.get_arguments()
+
+        exit(0)
+
+    def load_makefile(self):
+        dot = os.path.dirname(os.path.abspath(__file__))
+        f = open(os.path.join(dot, "dudac.Make"), "r")
+        self.dudac_makefile = f.read()
+        f.close
+
+    # This routine read the config.dudac configuration file which is
+    # optional inside every web service source code. It specifies the
+    # Monkey sources requirements and perform the right setup
+    def config_requirements(self):
+        ws = os.path.abspath(self.service)
+        if not os.path.isdir(ws):
+            print "Error: Invalid web service directory"
+            exit(1)
+
+        # Check if is an API level
+        try:
+            api_number = int(self.api_level)
+        except:
+            api_number = -1
+
+        if api_number > 0:
+            version = "dst-%i" % (api_number)
+        else:
+            version = self.api_level
+
+        mk_version      = version
+        mk_https_repo   = None
+        mk_git_repo     = None
+        duda_version    = version
+        duda_https_repo = None
+        duda_git_repo   = None
+
+        # Check if the service have a configuration file
+        config_file = os.path.abspath("%s/dudac.conf" % (ws))
+        if os.path.isfile(config_file) is True:
+            # Read the configuration
+            config = DudaConfig()
+            config.open(config_file)
+
+            for h in config.get_handlers():
+                if h == 'MONKEY':
+                    # Get key/values
+                    mk_version    = config.get_key(h, 'version')
+                    mk_https_repo = config.get_key(h, 'https_repo')
+                    mk_git_repo   = config.get_key(h, 'git_repo')
+                elif h == 'DUDA':
+                    duda_version    = config.get_key(h, 'version')
+                    duda_https_repo = config.get_key(h, 'https_repo')
+                    duda_git_repo   = config.get_key(h, 'git_repo')
+
+        # Configure repos
+        self.mk_git.setup(mk_version, mk_https_repo, mk_git_repo)
+        self.duda_git.setup(duda_version, duda_https_repo, duda_git_repo)
+
+    def update_framework(self, protocol):
+        self.mk_git.set_protocol(protocol)
+        self.duda_git.set_protocol(protocol)
+
+        if os.path.exists(self.mk_home):
+            self.mk_git.update(self.mk_home)
+        else:
+            self.mk_git.clone(self.mk_home)
+
+        if os.path.exists(self.duda_home):
+            self.duda_git.update(self.duda_home)
+        else:
+            self.duda_git.clone(self.duda_home)
+
+        # Cleanup and rebuild Monkey
+        cpath = os.getcwd()
+        os.chdir(self.mk_home)
+
+        self.mk_git.version = self.api_level
+        self.mk_git.snapshot()
+
+        self.duda_git.version = self.api_level
+        self.duda_git.snapshot()
+
+        self.merge_on_stage()
+        self.monkey.configure()
+
+        if os.path.exists("./Makefile"):
+            self.monkey.make_clean()
+
+        self.monkey.make_build()
+        os.chdir(cpath)
+
+    def merge_on_stage(self):
+        # Create archives from repos
+        self.mk_git.archive_to(self.stage_home + '/monkey')
+        self.duda_git.archive_to(self.stage_home + '/monkey/plugins/duda')
+
+        # Tag the content with branch used
+        f = open(self.stage_home + '/monkey/api_level.dudac', 'w')
+        f.write(self.mk_git.version + '\n')
+        f.close()
+
+
+    def run_webservice(self, schema=None):
+        ws = os.path.abspath(self.service)
+        monkey_stage = self.monkey.mk_path
+
+        # Check if the web service was staged and built previously, we
+        # this check to make sure the service is updated as the user needs/want
+        stage_level = self.stage_home + '/monkey/api_level.dudac'
+        if os.path.isfile(stage_level):
+            f = open(stage_level, 'r')
+            level = f.read().replace('\n', '')
+            f.close()
+
+            if level != self.mk_git.version:
+                self.rebuild_monkey = True
+        else:
+            self.rebuild_monkey = True
+
+        if self.rebuild_monkey is True:
+            # Backup our original path
+            cpath = os.getcwd()
+
+            # On rebuild, check that stack sources are in place
+            if os.path.exists(self.mk_home) is False or \
+               os.path.exists(self.duda_home) is False:
+                fail_msg("Error: the stack components are missing, try: \n\n" \
+                         "    $ dudac -s\n")
+                sys.exit(1)
+
+            # Make sure Monkey sources match the snapshot
+            if not os.getenv('DUDAC_STAGE_PATH'):
+                self.mk_git.snapshot()
+                self.duda_git.snapshot()
+                self.merge_on_stage()
+
+            # Cleanup and rebuild Monkey
+            os.chdir(monkey_stage)
+
+            if os.path.exists("./Makefile"):
+                self.monkey.make_clean()
+
+            self.monkey.configure()
+            self.monkey.make_build()
+
+            # Restore path
+            os.chdir(cpath)
+
+        makefile = "%s/Makefile" % (ws)
+        makefile_in = "%s/Makefile.in" % (ws)
+        if os.path.isdir(ws) is False or os.path.exists(makefile_in) is False:
+            print "Error: Invalid web service directory " + ws
+            exit(1)
+
+        # Monkey headers
+        mk_inc      = monkey_stage + "/src/include"
+        mk_duda     = monkey_stage + "/plugins/duda/src"
+	mk_packages = monkey_stage + "/plugins/duda/"
+
+        # Read the Makefile.in file
+        mk_ins = []
+        for root, dirs, files in os.walk(ws):
+            for file in files:
+                if file == 'Makefile.in':
+                    path = "%s/%s" % (root, file)
+                    mk_ins.append(root)
+
+        for mk in mk_ins:
+            mk_in = "%s/Makefile.in" % (mk)
+
+            CC_SET = None
+
+            f = open(mk_in, "r")
+            lines = f.readlines()
+            f.close()
+
+            raw = ""
+            for line in lines:
+                if line.startswith("INCDIR"):
+                    prev = line.replace("INCDIR", "")
+                    prev = prev.replace("=", "").strip()
+
+                    raw += "INCDIR  = " + prev
+                    raw += " -I" + mk_inc
+                    raw += " -I" + mk_duda
+                    raw += " -I" + mk_packages + "\n"
+                else:
+                    raw += line
+            raw += "\n"
+
+            makefile = "%s/Makefile" % (mk)
+            f_mk = open(makefile, "w")
+            f_mk.write("# Autogenerated by Duda Client Manager\n")
+            f_mk.write("# ====================================\n")
+
+            today = datetime.datetime.now()
+            f_mk.write("# Date      : " + today.strftime('%a %d, %b %Y at %H:%M') + "\n")
+            f_mk.write("# Stage Path: " + monkey_stage + "\n\n")
+
+            f_mk.write(raw)
+            f_mk.write(self.dudac_makefile)
+            f_mk.close()
+
+        # Cleanup web service and rebuild
+        execute("WebService  : clean", "make -C " + ws + " clean")
+        execute("WebService  : build", "make -C " + ws)
+
+        # Get services
+        services = []
+        list = os.listdir(ws)
+        for entry in list:
+            p = ws + "/" + entry
+            if os.path.isfile(p) and entry.endswith(".duda"):
+                e = {'name': entry[:-5], 'filename': entry}
+                services.append(e)
+
+        # check that services exists
+        if len(services) == 0:
+            print "Error: i could not find Duda services under", ws
+            exit(1)
+
+        # Setting up virtual host
+        vhost = monkey_stage + "/conf/sites/default"
+        f = open(vhost, "r")
+        lines = f.readlines()
+        f.close()
+
+        # Setting up web services
+        print "%s %-70s" % (MSG_NEW, "Monkey      : configure HTTP Server"),
+        raw = ""
+        for line in lines:
+            if line.startswith('[WEB_'):
+                break
+            else:
+                raw += line
+
+        for s in services:
+            raw += "[WEB_SERVICE]\n"
+            raw += "    Name " + s['name'] + "\n"
+            raw += "    Enabled on\n"
+
+            html = os.path.abspath(self.service) + '/html/'
+            if os.path.exists(html):
+                raw += "    DocumentRoot %s\n" % (html)
+
+            confdir = os.path.abspath(self.service) + '/conf/'
+            if os.path.exists(confdir):
+                raw += "    ConfDir %s\n" % (confdir)
+
+            datadir = os.path.abspath(self.service) + '/data/'
+            if os.path.exists(datadir):
+                raw += "    DataDir %s\n" % (datadir)
+
+            logdir = os.path.abspath(self.service) + '/logs/'
+            if os.path.exists(logdir):
+                raw += "    LogDir  %s\n" % (logdir)
+
+            raw += "\n"
+
+        f = open(vhost, "w")
+        f.write(raw)
+        f.close()
+
+        # Make sure Duda plugin is enabled on plugins.load
+        plugins = monkey_stage + "/conf/plugins.load"
+        f = open(plugins, "r")
+        lines = f.readlines()
+        f.close()
+
+        raw = ""
+        for line in lines:
+            if line.startswith("    # Load") and line.strip().endswith("-duda.so"):
+                line = "    " + line[6:]
+
+            raw += line
+
+        f = open(plugins, "w")
+        f.write(raw)
+        f.close()
+
+        # Setting up Duda plugin configuration
+        duda = monkey_stage + "/conf/plugins/duda/duda.conf"
+        f = open(duda, "r")
+        lines = f.readlines()
+        f.close()
+
+        raw = ""
+        for line in lines:
+            if line.startswith("    ServicesRoot"):
+                raw += "    ServicesRoot " + ws + "\n"
+            else:
+                raw += line
+
+        f = open(duda, "w")
+        f.write(raw)
+        f.close()
+
+        # Setting up Monkey
+        monkey = monkey_stage + "/conf/monkey.conf"
+        f = open(monkey, "r")
+        lines = f.readlines()
+        f.close()
+
+        raw = ""
+        for line in lines:
+            if line.startswith("    Port"):
+                raw += "    Port " + str(self.port) + "\n"
+            elif line.startswith("    User"):
+                raw += "    # User  Inactivated by DudaC\n"
+            elif line.startswith("    TransportLayer") and self.rebuild_monkey is True:
+                if self.SSL is True:
+                    raw += "    TransportLayer polarssl\n"
+                    self.SSL_default = True
+                else:
+                    raw += "    TransportLayer liana\n"
+                    self.SSL_default = False
+            elif line.startswith("    TransportLayer") and line.find('polarssl') > 0:
+                self.SSL_default = True
+                raw += line
+            else:
+                raw += line
+
+        f = open(monkey, "w")
+        f.write(raw)
+        f.close()
+
+        print MSG_OK
+
+        # Schema
+        # ======
+        # Once the new monkey.conf Port and TransportLayer are set, we can start
+        # overriding the config if we have a schema. We do this at the end to do not
+        # mess with specific settings handled by the previous lines.
+        if schema is not None:
+            # Change direct Port global
+            if 'Port' in schema:
+                self.port = schema['Port']
+
+            # open the file and read it content
+            f = open(monkey, "r")
+            lines = f.readlines()
+            f.close()
+
+            # compose a new buffer
+            raw = ""
+            for line in lines:
+                row = line
+                key = None
+                val = None
+                kv  = []
+                if line.startswith("    "):
+                    # strip the indentation
+                    row = line[4:]
+
+                    # Lets see if this line can belong to a commented key/value
+                    if row[:2] == '# ':
+                        arr = row[2:].split()
+                        if len(arr) == 2 and arr[0][-1] != ':':
+                            if arr[0] in self.MCONF_KNOWN:
+                                # ok, its a known key and is commented, now lets check
+                                # if this key is bein overriden through the schema
+                                if not arr[0] in schema:
+                                    raw += line
+                                else:
+                                    raw += "    %s %s\n" % (arr[0], schema[arr[0]])
+
+                                continue
+
+                    kv = row.split()
+                    if len(kv) != 2:
+                        raw += line
+                        continue
+                    else:
+                        # Start replacing
+                        key = kv[0]
+                        val = kv[1]
+                        if key in schema:
+                            raw += "    %s %s\n" % (key, schema[key])
+                            continue
+
+
+                raw += line
+
+            # Flush our new content
+            f = open(monkey, "w")
+            f.write(raw)
+            f.close()
+
+        # Configure Transport Layer for SSL
+        if self.SSL_default is True:
+            self.SSL_configure(monkey_stage)
+
+        http = monkey_stage + "bin/monkey"
+
+        try:
+            if self.SSL_default is True:
+                prot = "https"
+            else:
+                prot = "http"
+
+            d = 0
+            domain = prot + "://localhost:%s/" % str(self.port)
+            schema = ""
+            for s in services:
+                if d > 0:
+                    schema += "                                   "
+                schema += domain + services[d]['name'] + '/' + "\n"
+                d += 1
+
+            # Do not trap the output of the server, just print everything
+            # to STDOUT
+            sc = ANSI_RESET + ANSI_CYAN + schema + ANSI_RESET
+            if self.output_stdout is True:
+                execute_stdout("%s Service Up  : %s" \
+                                   % (MSG_NEW, sc), http, False)
+            else:
+                execute("%s Service Up  : %s" \
+                            % (MSG_NEW, sc), \
+                            http, False, True, False)
+
+        except (RuntimeError, TypeError, NameError):
+            print "\nDone!"
+            raise
+
+    def SSL_configure(self, monkey_stage):
+        plgs = monkey_stage + "/conf/plugins.load"
+        f = open(plgs, "r")
+        lines = f.readlines()
+        f.close()
+
+        raw = ""
+        for line in lines:
+            if line.find('monkey-polarssl') > 0 and self.SSL is True:
+                raw += line.replace("# Load", "Load")
+            else:
+                raw += line
+
+        f = open(plgs, "w")
+        f.write(raw)
+        f.close()
+
+        # Check if SSL certificates exists
+        sslconf = monkey_stage + "/conf/plugins/polarssl/polarssl.conf"
+        f = open(sslconf, "r")
+        lines = f.readlines()
+        f.close()
+
+        certificate_file = None
+        rsa_key_file = None
+        dh_param_file = None
+        for line in lines:
+            row = line.strip()
+            if row.startswith('CertificateFile'):
+                key, val = row.split()
+                if os.path.exists(val):
+                    certificate_file = val
+            elif row.startswith('RSAKeyFile'):
+                key, val = row.split()
+                if os.path.exists(val):
+                    rsa_key_file = val
+            elif row.startswith('DHParameterFile'):
+                key, val = row.split()
+                if os.path.exists(val):
+                    dh_param_file = val
+
+        # Generate Certificates if they dont exists
+        p = monkey_stage + "/conf/plugins/polarssl/"
+        if rsa_key_file is None:
+            cmd = "openssl genrsa -out " + p + "rsa_key.pem 1024"
+            execute("SSL: Generate RSA", cmd)
+            rsa_key_file = p + "rsa_key.pem"
+        else:
+            print_msg("SSL: RSA       (cached)", True)
+
+        if certificate_file is None:
+            cmd = "openssl req -new -x509 -key " + rsa_key_file + \
+                " -out " + p + "srv_cert.pem" + " -days 1095 -subj '/C=US'"
+            execute("SSL: Generate Certificate", cmd)
+            certificate_file = p + "srv_cert.pem"
+        else:
+            print_msg("SSL: Cert      (cached)", True)
+
+        if dh_param_file is None:
+            cmd = "openssl dhparam -out " + p + "dhparam.pem 1024"
+            execute("SSL: Generate DH Params", cmd)
+            dh_param_file = p + "dhparam.pem"
+        else:
+            print_msg("SSL: DH Params (cached)", True)
+
+        # Create new config and override polarssl.conf file
+        raw  = "[SSL]\n"
+        raw += "    CertificateFile " + certificate_file + "\n"
+        raw += "    RSAKeyFile      " + rsa_key_file + "\n"
+        raw += "    DHParameterFile " + dh_param_file + "\n\n"
+
+        f = open(sslconf, "w")
+        f.write(raw)
+        f.close()
+
+    def reset(self):
+        if self.dudac_stage_path is None:
+            self.mk_git.remove(self.mk_home)
+            self.duda_git.remove(self.duda_home)
+
+    def print_version(self):
+        print_bold("Duda Client Manager - v%s" % DUDAC_VERSION)
+        print_color("http://duda.io", ANSI_YELLOW, True)
+        print_color("http://monkey-project.com\n", ANSI_YELLOW, True)
+
+    def print_help(self):
+        print "Usage: dudac [-g|-s] [-V] [-S] [-h] [-v] [-A] [-J] [-T] -w WEB_SERVICE_PATH\n"
+        print ANSI_BOLD + ANSI_WHITE + "Stack Build Options" + ANSI_RESET
+        print "  -V\t\t\tAPI level (default: %i)" % DEFAULT_API_LEVEL
+        print "  -s\t\t\tGet stack sources using HTTPS"
+        print "  -g\t\t\tGet stack sources using GIT protocol (SSH)"
+        print "  -F\t\t\tForce mode, rebuild the Stage area"
+        print "  -r\t\t\tReset environment"
+
+        print
+        print ANSI_BOLD + ANSI_WHITE + "Profiling and Trace" + ANSI_RESET
+        print "  -A\t\t\tUse libc memory allocator instead of Jemalloc (disabled)"
+        print "  -X\t\t\tEnable Jemalloc statistics (disabled)"
+        print "  -J\t\t\tEnable Jemalloc profiling and leaks detection (disabled)"
+        print "  -T\t\t\tEnable Linux Trace Toolkit (disabled)"
+
+        print
+        print ANSI_BOLD + ANSI_WHITE + "HTTP Server Options" + ANSI_RESET
+        print "  -p TCP_PORT\t\tSet TCP port (default 2001)"
+        print "  -w WEB_SERVICE\tSpecify web service source path"
+        print "  -S\t\t\tWeb Service will run with SSL mode enabled"
+        print "  -M 'k1=v1,kn=vn'\tOverride some web server config key/value"
+
+        print
+        print ANSI_BOLD + ANSI_WHITE + "Others" + ANSI_RESET
+        print "  -h\t\t\tPrint this help"
+        print "  -u\t\t\tRedirect server output to STDOUT"
+        print "  -v\t\t\tPrint version"
+
+    # it creates a configuration schema to override the values of the main
+    # Monkey configuration file
+    def conf_schema(self, value):
+        if value.find(',') > 0:
+            entries = value.split(',')
+            if len(entries) == 0:
+                return None
+        else:
+            key, val = value.split('=')
+            if len(key) == 0 or len(val) == 0:
+                print "Error: Invalid configuration schema '%s'" % value
+                exit(1)
+
+            return {key: val}
+
+        # Lookup all entries in the value
+        c = {}
+        for e in entries:
+            key, val = e.split('=')
+            if len(key) == 0 or len(val) == 0:
+                print "Error: Invalid configuration schema '%s'" % value
+                exit(1)
+            c[key] = val
+
+        return c
+
+    def get_arguments(self):
+        update = None
+        monkey_conf = None
+        self.api_level = "dst-%i" % int(DEFAULT_API_LEVEL)
+
+        # Reading command line arguments
+        try:
+            optlist, args = getopt.getopt(sys.argv[1:], 'V:sgFrhvSuw:p:AXJTM:')
+        except getopt.GetoptError:
+            self.print_help()
+            sys.exit(2)
+
+        if len(optlist) == 0:
+            self.print_help()
+
+        # Check options
+        for op, arg in optlist:
+            if op == '-s':
+                update = PROTOCOL_HTTPS
+            elif op == '-g':
+                update = PROTOCOL_GIT
+            elif op == '-F':
+                self.rebuild_monkey = True
+            elif op == '-r':
+                self.reset()
+                exit(0)
+            elif op == '-V':
+                self.api_level = arg
+            elif op == '-S':
+                self.SSL = True
+            elif op == '-h':
+                self.print_help()
+                sys.exit(0)
+            elif op == '-p':
+                if not str(arg).isdigit():
+                    self.print_help()
+                    exit(1)
+                self.port = arg
+            elif op == '-A':
+                self.linux_malloc = True
+            elif op == '-X':
+                self.jemalloc_stats = True
+            elif op == '-J':
+                self.jemalloc_prof = True
+            elif op == '-M':
+                monkey_conf = arg
+            elif op == '-D':
+                self.service_macros = arg
+            elif op == '-T':
+                self.linux_trace = True
+            elif op == '-u':
+                self.output_stdout = True
+            elif op == '-v':
+                sys.exit(0)
+                break
+            elif op == '-w':
+                self.service = arg
+                self.config_requirements()
+
+        # SSL
+        if self.SSL is True:
+            self.monkey.SSL = True
+
+        # Enable Jemalloc profiling
+        if 'JEMALLOC_OPTS' not in os.environ:
+            os.environ['JEMALLOC_OPTS'] = ''
+
+        if self.jemalloc_prof is True:
+            os.environ['JEMALLOC_OPTS']  += '--enable-prof'
+            os.environ['JE_MALLOC_CONF']  = 'prof_leak:true,prof:true,prof_prefix:duda.jeprof'
+
+        # More environment vars: make will use 8 jobs
+        os.environ['MAKEFLAGS'] = '-j 8'
+
+        # Linux Trace Toolkit
+        if self.linux_trace is True:
+            self.monkey.opts += "--linux-trace "
+
+        # Use system malloc instead Jemalloc
+        if self.linux_malloc is True:
+            self.monkey.opts += "--malloc-libc "
+
+        # Jemalloc Stats
+        if self.linux_malloc is False and self.jemalloc_stats is True:
+            os.environ['JEMALLOC_OPTS'] += ' --enable-stats'
+
+        # Rebuild the stack ?
+        if update is not None:
+            if self.rebuild_monkey is True:
+                print "Error: you cannot mix the flag -f with -g or -s"
+                exit(1)
+
+            if self.dudac_stage_path is None:
+                self.update_framework(update)
+            else:
+                print_msg("DUDAC_STAGE_PATH is set")
+
+        # Override Monkey configuration. It will create the configuration
+        # schema which is used later by the run_webservice() method.
+        if monkey_conf:
+            mconf_schema = self.conf_schema(monkey_conf)
+        else:
+            mconf_schema = None
+
+        # Run web service
+        if self.service:
+            self.run_webservice(mconf_schema)
+
+    def _temp_path(self):
+        pass
+
+def main():
+    d = Duda()
+
+if __name__ == '__main__':
+    main()

+ 277 - 0
duda/dudac/dudaclient/utils.py

@@ -0,0 +1,277 @@
+# Copyright (C) 2012, Eduardo Silva <[email protected]>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+
+import os
+import sys
+import time
+import commands
+
+# BUILD: Set the default branch to dst-1 (Duda Stable API Level 1)
+DEFAULT_API_LEVEL = 1
+
+# ANSI Colors
+ANSI_BOLD      = "\033[1m"
+ANSI_CYAN      = "\033[36m"
+ANSI_MAGENTA   = "\033[35m"
+ANSI_RED     = "\033[31m"
+ANSI_YELLOW  = "\033[33m"
+ANSI_BLUE    = "\033[34m"
+ANSI_GREEN   = "\033[32m"
+ANSI_WHITE   = "\033[37m"
+ANSI_RESET   = "\033[0m"
+
+MSG_TAG_INIT = ANSI_RESET + ANSI_BOLD + '['
+MSG_TAG_END  = ANSI_RESET + ANSI_BOLD + ']' + ANSI_RESET
+MSG_OK       = MSG_TAG_INIT + ANSI_GREEN + 'OK' + MSG_TAG_END
+MSG_FAIL     = MSG_TAG_INIT + ANSI_RED + 'FAILED' + MSG_TAG_END
+MSG_NEW      = '[' + ANSI_YELLOW + '+' + ANSI_RESET + ']'
+
+# Print a failure message
+def fail_msg(msg):
+    print ANSI_RED + "[-] " + ANSI_RESET + msg
+
+# Determinate if the system create coredumps with PID or not
+def core_with_pid():
+    f = open('/proc/sys/kernel/core_uses_pid')
+    data = f.read(1)
+    f.close()
+
+    if data == '0':
+        return False
+
+    return True
+
+def gdb_trace(command, core):
+    next_highlight = False
+    gdb = 'gdb --batch -ex bt --core=' + core + ' ' + command
+    sys.stdout.flush()
+    ret = commands.getstatusoutput(gdb)
+    if ret[0] == 0:
+        fail_msg('Stack Trace lookup')
+        os.write(0, ANSI_YELLOW)
+        for line in ret[1].split('\n'):
+            if len(line) < 2:
+                continue
+
+            if line[0] == '#':
+                if next_highlight is True:
+                    print ANSI_BOLD + ANSI_YELLOW + '    ' + \
+                        line + ANSI_RESET + ANSI_YELLOW
+                    next_highlight = False
+                    continue
+
+                if line.find('<signal handler called>') > 0:
+                    next_highlight = True
+
+                print '    ' + line
+
+        os.write(0, ANSI_RESET)
+
+    return ret[1]
+
+def output_pid(out):
+    pid = None
+
+    # Get Monkey PID
+    for line in out.split('\n'):
+        if line.find('Process ID') > 0:
+            arr = line.split()
+            pid = arr[-1]
+
+    return pid
+
+def gdb_analyze(command, out):
+
+    pid = output_pid(out)
+    if pid is None:
+        return None
+
+    # Check if a core dump file exists
+    if out.find('[stack trace]') > 0 or out.find('backtrace'):
+        fail_msg("Crash detected, trying to find some core dump for PID " + pid)
+        if core_with_pid() is False:
+            core_file = 'core'
+        else:
+            core_file = 'core.' + pid
+
+        if os.path.isfile(core_file) is True:
+            fail_msg('Core dump found: \'' + core_file + '\'')
+            ret = gdb_trace(command, core_file)
+            return ret
+        else:
+            fail_msg('No core dump was found:')
+
+            # Check ulimit value
+            ret = commands.getstatusoutput('ulimit -c')
+            if ret[1] != 'unlimited':
+                print ANSI_YELLOW + '    --'
+                print ANSI_YELLOW + '    Enable core dumps with:'
+                print
+                print '        $ ulimit -c unlimited'
+                print '    --' + ANSI_RESET
+
+            return None
+
+# Execute a command and print the output to stdout
+def execute_stdout(header, command, head=True):
+    if head is True:
+        print "%s %-70s" % (MSG_NEW, header),
+    else:
+        print header
+
+    sys.stdout.flush()
+    os.system(command)
+
+# Execute a command and trap the return value
+def execute(header, command, status=True, crash_debug=False, head=True):
+    if head is True:
+        print "%s %-70s" % (MSG_NEW, header),
+    else:
+        print header
+
+    sys.stdout.flush()
+
+    ret = commands.getstatusoutput(command)
+    if os.WEXITSTATUS(ret[0]) == 0:
+        if status is True:
+            print MSG_OK
+        if ret[1].find('warning') > 0:
+            print ANSI_BOLD + ANSI_RED + "--- Compiler Warnings ---" + ANSI_RESET
+
+            lines = ret[1].split('\n')
+            for l in lines:
+                if l.find('warning') > 0:
+                    print ANSI_GREEN + l + ANSI_RESET
+
+            print ANSI_BOLD + ANSI_RED + "--- * --- * --- * --- * ---"
+            print ANSI_RESET
+    else:
+        if status is True:
+            print MSG_FAIL
+
+        # The tricky part: what's the real process return status ?, according
+        # to Python documentation the value or ret[0] represents the following:
+        #
+        # "The exit status for the command can be interpreted according to the
+        #  rules for the C function wait()."
+        #
+        # what ?, back to C manpages:
+        #
+        #  This integer can be inspected with the following macros (which take
+        #  the integer itself as  an  argument,  not a pointer to it, as is
+        #  done in wait() and waitpid()!):
+        #
+        #  WIFEXITED(status)...
+        #  WEXITSTATUS(status)...
+        #  WIFSIGNALED(status)...
+        #  WTERMSIG(status)...
+        #  WCOREDUMP(status)...
+        #  WIFSTOPPED(status)...
+        #  WSTOPSIG(status)...
+        #  WIFCONTINUED(status)...
+        #
+        # ok, so where are those macros on Python ??, Google -> Python WIFEXITED:
+        #
+        #   => os.WIFEXITED
+        #
+        # So everything i wanted to know was in the 'os' package, so why you tell
+        # me to go to C man page ?..lovely Python...
+        #
+
+        print
+        fail_msg("Command exit (status=%i): %s" % (os.WEXITSTATUS(ret[0]), command))
+
+
+        status = ret[0]
+
+        """
+        print "WIFEXITED=", os.WIFEXITED(status)
+        print "WEXITSTATUS=", os.WEXITSTATUS(status)
+        print "WIFSIGNALED=", os.WIFSIGNALED(status)
+        print "WTERMSIG=", os.WTERMSIG(status)
+        print "WCOREDUMP=", os.WCOREDUMP(status)
+        print "WIFSTOPPED=", os.WIFSTOPPED(status)
+        print "WSTOPSIG=", os.WSTOPSIG(status)
+        print "WIFCONTINUED=", os.WIFCONTINUED(status)
+        """
+
+        if os.WIFSIGNALED(ret[0]) is False:
+            print ANSI_YELLOW + '-------------------------------' + ANSI_RESET
+            print ret[1]
+            print ANSI_YELLOW + '-------------------------------' + ANSI_RESET
+
+        if os.WEXITSTATUS(status) < 134: # 128 base + 11 SIGSEV
+            exit(1)
+
+        crash_ret = None
+        if crash_debug is True:
+            crash_ret = gdb_analyze(command, ret[1])
+
+
+        # Store outout to log file
+        pid = output_pid(ret[1])
+        if pid is not None:
+            target = 'logs/crash_report.' + pid
+        else:
+            print ret[1]
+            exit(1)
+
+        report = time.strftime('%Y/%m/%d %H:%M:%S: ')
+        report += 'Duda I/O Web Service Crash Report\n'
+        report += '======================================================\n\n'
+        report += '>>>>>>>>>>>>>>>> HTTP Server Output <<<<<<<<<<<<<<<<\n'
+        report += '                 ^^^^^^^^^^^^^^^^^^\n\n'
+        report += ret[1]
+
+        report += '\n\n'
+        report += '>>>>>>>>>>>>>>>> Stack Trace Analysis <<<<<<<<<<<<<<<<\n'
+        report += '                 ^^^^^^^^^^^^^^^^^^^^\n\n'
+
+        if crash_ret is None:
+            report += 'Error: core dump file not found.\n'
+        else:
+            report += crash_ret
+            report += '\n'
+
+        f = open(target, 'wa+')
+        f.write(report)
+        f.close()
+
+        fail_msg("Crash report saved at " + target)
+        exit(1)
+
+    return ret
+
+def print_msg(msg, status = 0):
+    print "[+] %-30s" % (msg),
+
+    if status == 0:
+        print "[FAILED]\n"
+    else:
+        print "[OK]"
+
+def print_bold(msg):
+    print ANSI_BOLD + msg + ANSI_RESET
+
+def print_color(msg, color, is_bold=False):
+    text = color + msg + ANSI_RESET
+    if is_bold is True:
+        text = ANSI_BOLD + text
+
+    print text
+
+def print_entry(header):
+    print "%s %s" % (MSG_NEW, header)

+ 43 - 0
duda/dudac/setup.py

@@ -0,0 +1,43 @@
+import os
+from setuptools import setup, find_packages
+
+
+def read(fname):
+    return open(os.path.join(os.path.dirname(__file__), fname)).read()
+
+
+setup(
+    name="dudac",
+    version="0.22",
+    author="Eduardo Silva",
+    author_email="[email protected]",
+    description=("DudaC is a command line interface for Duda I/O web services "
+                 "framework"),
+    license="LGPLv2",
+    keywords="HTTP monkey duda server framework linux websocket nosql",
+    url="http://duda.io",
+    download_url="http://duda.io/releases/duda-client/dudac-0.22.tar.gz",
+    packages=find_packages(),
+    long_description=read('README'),
+    include_package_data=True,
+    classifiers = [
+        #'Development Status :: 1 - Planning',
+        #'Development Status :: 2 - Pre-Alpha',
+        #'Development Status :: 3 - Alpha',
+        #'Development Status :: 4 - Beta',
+        'Development Status :: 5 - Production/Stable',
+        #'Development Status :: 6 - Mature',
+        #'Development Status :: 7 - Inactive',
+        'License :: OSI Approved :: GNU Lesser General Public License v2 (LGPLv2)',
+        'Programming Language :: Python',
+        'Environment :: Console',
+        'Intended Audience :: Developers',
+        'Topic :: Software Development :: Build Tools',
+    ],
+    zip_safe=False,
+    entry_points={
+        'console_scripts': [
+            'dudac = dudaclient.main:main'
+        ]
+    },
+)

+ 4 - 0
duda/run_application

@@ -0,0 +1,4 @@
+cd dudac/
+./dudac -r
+./dudac -s
+./dudac -w ../webservice -p 2001

+ 17 - 0
duda/setup.py

@@ -0,0 +1,17 @@
+import subprocess
+import sys
+import os
+import setup_util 
+
+def start(args, logfile, errfile):
+  subprocess.Popen("./run_application", shell=True, stderr=errfile, stdout=logfile);
+  return 0
+
+def stop(logfile, errfile):
+  p = subprocess.Popen(['ps', 'aux'], stdout=subprocess.PIPE)
+  out, err = p.communicate()
+  for line in out.splitlines():
+    if 'monkey' in line:
+      pid = int(line.split(None, 2)[1])
+      os.kill(pid, 15)
+  return 0

+ 7 - 0
duda/webservice/Makefile.in

@@ -0,0 +1,7 @@
+NAME    = ws
+CC      = gcc
+CFLAGS  = -g -Wall -O2
+LDFLAGS =
+DEFS    =
+INCDIR  =
+OBJECTS = main.o

+ 78 - 0
duda/webservice/main.c

@@ -0,0 +1,78 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/*
+ * Duda I/O Benchmark Tests
+ * ========================
+ * This web service is made for the performance contest made by
+ * TechEmpower, mode details here:
+ *
+ *     http://www.techempower.com/benchmarks
+ *
+ * At the moment only Tests 1 & 6 are implemented.
+ */
+
+#include "webservice.h"
+#include "packages/json/json.h"
+
+/* Test Macros (Tn) */
+#define JSON_CONTENT_TYPE    "Content-Type: application/json"
+#define PLAIN_CONTENT_TYPE   "Content-Type: text/plain"
+#define T6_BODY              "Hello, World!"
+
+DUDA_REGISTER("Duda I/O Benchmark Test", "WS Bench");
+
+/*
+ * Test type 1: JSON serialization
+ * ===============================
+ * This test use the JSON API object to compose the JSON response
+ */
+void cb_json(duda_request_t *dr)
+{
+    char *body;
+    int body_len;
+    json_t *j_root;
+
+    /* Instance the JSON object and compose the content */
+    j_root = json->create_object();
+    json->add_to_object(j_root,
+                        "message",
+                        json->create_string("Hello, World!"));
+
+    /* Format output to string */
+    body = json->print_unformatted_gc(dr, j_root);
+    body_len = strlen(body);
+
+    /* Compose the response */
+    response->http_status(dr, 200);
+    response->http_header_n(dr, JSON_CONTENT_TYPE, sizeof(JSON_CONTENT_TYPE) - 1);
+    response->print(dr, body, body_len);
+    response->end(dr, NULL);
+}
+
+
+/*
+ * Test type 6: Plaintext
+ * ======================
+ */
+void cb_plaintext(duda_request_t *dr)
+{
+    response->http_status(dr, 200);
+    response->http_header_n(dr, PLAIN_CONTENT_TYPE, sizeof(PLAIN_CONTENT_TYPE) - 1);
+    response->print(dr, T6_BODY, sizeof(T6_BODY) - 1);
+    response->end(dr, NULL);
+}
+
+int duda_main()
+{
+    /* load packages */
+    duda_load_package(json, "json");
+
+    /* let this web service own the virtual host */
+    conf->service_root();
+
+    /* set callbacks */
+    map->static_add("/json", "cb_json");            /* Test #1 */
+    map->static_add("/plaintext", "cb_plaintext");  /* Test #6 */
+
+    return 0;
+}