Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMartin Abente <martin.abente.lahaye@gmail.com>2011-01-18 19:13:20 (GMT)
committer Martin Abente <martin.abente.lahaye@gmail.com>2011-01-18 19:13:20 (GMT)
commit9e96098f0ef7a9da0ef7f95660ed97e77d6ecaa1 (patch)
treed754887aee98772f86776d72c1b5f189bc2cff04
Initial commit
-rw-r--r--README9
-rw-r--r--config.ini10
-rw-r--r--daemon.py144
-rwxr-xr-xfbdaemon.py45
-rw-r--r--fbserver.py114
-rw-r--r--fbserver.spec55
-rw-r--r--fdserverd51
7 files changed, 428 insertions, 0 deletions
diff --git a/README b/README
new file mode 100644
index 0000000..8381129
--- /dev/null
+++ b/README
@@ -0,0 +1,9 @@
+INSTALLATION:
+1. Create private key and certificate:
+ openssl req -new -x509 -days 99999 -nodes -out cert.pem -keyout pkey.pem
+
+2. Modify config.ini and complete information (USE ABSOLUTE PATHS).
+ vim config.ini
+
+USAGE:
+ /etc/init.d/fbserverd start
diff --git a/config.ini b/config.ini
new file mode 100644
index 0000000..2f0ff7c
--- /dev/null
+++ b/config.ini
@@ -0,0 +1,10 @@
+[server]
+host: localhost
+port: 8080
+
+[ssl]
+pkey_file: /home/tch/Devel/feedback/pkey-cert.pem
+cert_file: /home/tch/Devel/feedback/pkey-cert.pem
+
+[feedback]
+reports_path: /home/tch/Devel/feedback/reports/
diff --git a/daemon.py b/daemon.py
new file mode 100644
index 0000000..f9cdb91
--- /dev/null
+++ b/daemon.py
@@ -0,0 +1,144 @@
+#!/usr/bin/env python
+
+# This source code is based on Sander Marechal's examples provided at
+# http://www.jejik.com/articles/2007/02/a_simple_unix_linux_daemon_in_python/
+
+import sys, os, time, atexit
+from signal import SIGTERM
+
+class Daemon:
+ """
+ A generic daemon class.
+
+ Usage: subclass the Daemon class and override the run() method
+ """
+ def __init__(self, pidfile, stdin='/dev/null', stdout='/dev/null', stderr='/dev/null'):
+ self.stdin = stdin
+ self.stdout = stdout
+ self.stderr = stderr
+ self.pidfile = pidfile
+
+ def daemonize(self):
+ """
+ do the UNIX double-fork magic, see Stevens' "Advanced
+ Programming in the UNIX Environment" for details (ISBN 0201563177)
+ http://www.erlenstar.demon.co.uk/unix/faq_2.html#SEC16
+ """
+ try:
+ pid = os.fork()
+ if pid > 0:
+ # exit first parent
+ sys.exit(0)
+ except OSError, e:
+ sys.stderr.write("fork #1 failed: %d (%s)\n" % (e.errno, e.strerror))
+ sys.exit(1)
+
+ # decouple from parent environment
+ os.chdir("/")
+ os.setsid()
+ os.umask(0)
+
+ # do second fork
+ try:
+ pid = os.fork()
+ if pid > 0:
+ # exit from second parent
+ sys.exit(0)
+ except OSError, e:
+ sys.stderr.write("fork #2 failed: %d (%s)\n" % (e.errno, e.strerror))
+ sys.exit(1)
+
+ # redirect standard file descriptors
+ sys.stdout.flush()
+ sys.stderr.flush()
+ si = file(self.stdin, 'r')
+ so = file(self.stdout, 'a+')
+ se = file(self.stderr, 'a+', 0)
+ os.dup2(si.fileno(), sys.stdin.fileno())
+ os.dup2(so.fileno(), sys.stdout.fileno())
+ os.dup2(se.fileno(), sys.stderr.fileno())
+
+ # write pidfile
+ atexit.register(self.delpid)
+ pid = str(os.getpid())
+ file(self.pidfile,'w+').write("%s\n" % pid)
+
+ def delpid(self):
+ os.remove(self.pidfile)
+
+ def start(self):
+ """
+ Start the daemon
+ """
+ if self.running():
+ message = "pidfile %s already exist. Daemon already running?\n"
+ sys.stderr.write(message % self.pidfile)
+ sys.exit(1)
+
+ # Start the daemon
+ self.daemonize()
+ self.run()
+
+ def status(self):
+ pid = self.running()
+ if pid:
+ message = "Running with pid %s\n" % pid
+ else:
+ message = "Is not running.\n"
+
+ sys.stderr.write(message)
+
+ def running(self):
+ # Check for a pidfile to see if the daemon already runs
+ try:
+ pf = file(self.pidfile,'r')
+ pid = int(pf.read().strip())
+ pf.close()
+ except IOError:
+ pid = None
+
+ return pid
+
+ def stop(self):
+ """
+ Stop the daemon
+ """
+ # Get the pid from the pidfile
+ try:
+ pf = file(self.pidfile,'r')
+ pid = int(pf.read().strip())
+ pf.close()
+ except IOError:
+ pid = None
+
+ if not pid:
+ message = "pidfile %s does not exist. Daemon not running?\n"
+ sys.stderr.write(message % self.pidfile)
+ return # not an error in a restart
+
+ # Try killing the daemon process
+ try:
+ while 1:
+ os.kill(pid, SIGTERM)
+ time.sleep(0.1)
+ except OSError, err:
+ err = str(err)
+ if err.find("No such process") > 0:
+ if os.path.exists(self.pidfile):
+ os.remove(self.pidfile)
+ else:
+ print str(err)
+ sys.exit(1)
+
+ def restart(self):
+ """
+ Restart the daemon
+ """
+ self.stop()
+ self.start()
+
+ def run(self):
+ """
+ You should override this method when you subclass Daemon. It will be called after the process has been
+ daemonized by start() or restart().
+ """
diff --git a/fbdaemon.py b/fbdaemon.py
new file mode 100755
index 0000000..7e96177
--- /dev/null
+++ b/fbdaemon.py
@@ -0,0 +1,45 @@
+#!/usr/bin/env python
+# Copyright (C) 2011, Martin Abente
+#
+# 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, see <http://www.gnu.org/licenses/>
+
+# This source code is based on Sander Marechal's examples provided at
+# http://www.jejik.com/articles/2007/02/a_simple_unix_linux_daemon_in_python/
+
+import sys
+import fbserver
+from daemon import Daemon
+
+class FeedbackDaemon(Daemon):
+ def run(self):
+ fbserver.listen()
+
+if __name__ == "__main__":
+ daemon = FeedbackDaemon('/tmp/fbdaemon.pid')
+ if len(sys.argv) == 2:
+ if 'start' == sys.argv[1]:
+ daemon.start()
+ elif 'stop' == sys.argv[1]:
+ daemon.stop()
+ elif 'restart' == sys.argv[1]:
+ daemon.restart()
+ elif 'status' == sys.argv[1]:
+ daemon.status()
+ else:
+ print "Unknown command"
+ sys.exit(2)
+ sys.exit(0)
+ else:
+ print "usage: %s start|stop|restart" % sys.argv[0]
+ sys.exit(2)
diff --git a/fbserver.py b/fbserver.py
new file mode 100644
index 0000000..3a9ce60
--- /dev/null
+++ b/fbserver.py
@@ -0,0 +1,114 @@
+# Copyright (C) 2011, Aleksey Lim
+# Copyright (C) 2011, Martin Abente
+#
+# 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, see <http://www.gnu.org/licenses/>.
+
+import os
+import cgi
+import time
+import socket
+import logging
+import tarfile
+import threading
+from ConfigParser import ConfigParser
+from cStringIO import StringIO
+from SocketServer import ThreadingMixIn, TCPServer, BaseServer
+from SimpleHTTPServer import SimpleHTTPRequestHandler
+from OpenSSL import SSL
+
+config = ConfigParser()
+config.read('config.ini')
+
+HOST = config.get('server', 'host')
+PORT = config.getint('server', 'port')
+PKEY_FILE = config.get('ssl', 'pkey_file')
+CERT_FILE = config.get('ssl', 'cert_file')
+REPORTS_PATH = config.get('feedback', 'reports_path')
+
+class RequestHandler(SimpleHTTPRequestHandler):
+
+ def setup(self):
+ self.connection = self.request
+ self.rfile = socket._fileobject(self.request, 'rb', self.rbufsize)
+ self.wfile = socket._fileobject(self.request, 'wb', self.wbufsize)
+
+ def do_GET(self):
+ self._reply(403, 'Only POST requests are allowed')
+
+ def do_POST(self):
+ ctype, pdict = cgi.parse_header(self.headers.getheader('content-type'))
+
+ if ctype != 'multipart/form-data':
+ self._reply(403, 'Only multipart/form-data is accepted')
+ return
+
+ body = cgi.parse_multipart(self.rfile, pdict)
+ tar_content = body.get('report')
+ if not tar_content:
+ self._reply(403, 'No "report" form parameter')
+ return
+
+ try:
+ tar = tarfile.open(fileobj=StringIO(tar_content[0]), mode='r:gz')
+ report = tar.extractfile('report').read()
+ self._save_report(report)
+ self._reply(200, 'Report was accepted')
+ except Exception:
+ logging.exception('Cannot process request')
+ self._reply(403, 'Cannot process request')
+
+ def _reply(self, code, message):
+ self.send_response(code)
+ self.send_header('Content-type', 'text/html')
+ self.send_header('Content-length', len(message))
+ self.end_headers()
+
+ self.wfile.write(message)
+
+ self.wfile.flush()
+ self.connection.shutdown()
+
+ def _save_report(self, report):
+ report_path = self._unique_path()
+ open(report_path, 'w').write(report)
+
+ def _unique_path(self):
+ base_name = time.strftime('%Y-%m-%d-%s')
+ counter = 0
+ while True:
+ attempt = '%s-%s' % (base_name, str(counter))
+ path = os.path.join(REPORTS_PATH, attempt)
+ if not os.path.exists(path):
+ return path
+ counter += 1
+
+class Server(ThreadingMixIn, TCPServer):
+
+ def __init__(self, server_address, HandlerClass):
+ BaseServer.__init__(self, server_address, HandlerClass)
+
+ ctx = SSL.Context(SSL.SSLv23_METHOD)
+ ctx.use_privatekey_file(PKEY_FILE)
+ ctx.use_certificate_file(CERT_FILE)
+ self.socket = SSL.Connection(ctx,
+ socket.socket(self.address_family, self.socket_type))
+
+ self.allow_reuse_address = True
+ self.server_bind()
+ self.server_activate()
+
+def listen():
+ srv = Server((HOST, PORT), RequestHandler)
+ srv_thread = threading.Thread(target=srv.serve_forever)
+ srv_thread.start()
diff --git a/fbserver.spec b/fbserver.spec
new file mode 100644
index 0000000..ae352d3
--- /dev/null
+++ b/fbserver.spec
@@ -0,0 +1,55 @@
+Name: sugar-fbserver
+Version: 0.1
+Release: 1
+Vendor: Activity Central
+Summary: Sugar debugging feedback server
+Group: Applications/Internet
+License: GPL
+URL: http://git.sugarlabs.org
+Source0: %{name}-%{version}.tar.gz
+BuildRoot: %(mktemp -ud %{_tmppath}/%{name}-%{version}-%{release}-XXXXXX)
+Requires: python
+BuildArch: noarch
+
+%description
+Recieves debugging information from sugar clients.
+
+%prep
+%setup -q
+
+%build
+%install
+rm -rf $RPM_BUILD_ROOT
+
+mkdir -p $RPM_BUILD_ROOT/opt/%{name}
+cp -r * $RPM_BUILD_ROOT/opt/%{name}
+
+mkdir -p $RPM_BUILD_ROOT/etc/init.d/
+cp fbserverd $RPM_BUILD_ROOT/etc/init.d/
+
+# kill packaging
+rm $RPM_BUILD_ROOT/opt/%{name}/fbserver.spec
+rm $RPM_BUILD_ROOT/opt/%{name}/fbserverd
+
+%clean
+rm -rf $RPM_BUILD_ROOT
+
+%post
+chkconfig --level 345 fbserverd on
+
+%preun
+chkconfig --level 345 fbserverd off
+
+%postun
+
+%files
+%defattr(-,root,root,-)
+%dir /opt/%{name}
+/opt/%{name}/
+%attr(755,root,root) /etc/init.d/fbserverd
+
+%changelog
+
+* Tue Jan 18 2011 Martin Abente. <mabente@paraguayeduca.org>
+- Initial project
+
diff --git a/fdserverd b/fdserverd
new file mode 100644
index 0000000..2a0082d
--- /dev/null
+++ b/fdserverd
@@ -0,0 +1,51 @@
+#!/bin/bash
+# feedback server daemon launcher
+#
+# chkconfig: - 91 24
+# description: Feedback server daemon
+# pidfile: /opt/fbserver/fbdaemon.pid
+
+# Source function library.
+. /etc/init.d/functions
+
+start() {
+ echo "Starting feedback server: "
+ /opt/fbserver/fbdaemon.py start
+ return 0
+}
+
+stop() {
+ echo "Stopping feedback server: "
+ /opt/fbserver/fbdaemon.py stop
+ return 0
+}
+
+status() {
+ /opt/fbserver/fbdaemon.py status
+ return 0
+}
+
+restart() {
+ stop
+ start
+}
+
+case "$1" in
+ start)
+ start
+ ;;
+ status)
+ status
+ ;;
+ stop)
+ stop
+ ;;
+ restart|reload)
+ restart
+ ;;
+ *)
+ echo "*** Usage: {start|stop|restart}"
+ exit 1
+esac
+
+exit $?