Permalink
Browse files

daemonize it.

use python-daemon library to daemonize ftp-cloudfs.
  • Loading branch information...
1 parent 12c7c85 commit 9b39540599e634e2d909312a5421e005b3db39c6 Chmouel Boudjnah committed Feb 19, 2011
Showing with 217 additions and 99 deletions.
  1. +2 −1 README.rst
  2. +3 −92 bin/ftpcloudfs
  3. +28 −6 debian/init
  4. +161 −0 ftpcloudfs/main.py
  5. +23 −0 ftpcloudfs/utils.py
View
@@ -20,9 +20,10 @@ a non root/administrator user.
REQUIREMENT
===========
-- Python >= 2.5
+- Python >= 2.6 (probably 2.5 as well but not extensively tested)
- python-cloudfiles >= 1.3.0 - http://github.com/rackspace/python-cloudfiles
- pyftpdlib >= 0.6.0 - http://code.google.com/p/pyftpdlib/
+- python-daemon >= 1.6 - http://pypi.python.org/pypi/python-daemon/
Operating Systems
=================
View
@@ -4,104 +4,15 @@ FTP Cloud Files - A FTP Proxy to Cloud FIles.
"""
import sys
import os
-import logging
-from optparse import OptionParser
-from socket import gethostbyname, gaierror
-
-from pyftpdlib import ftpserver
-
-# Make it easier for testing add the parent directory of this script
-# so we get pyftpdlib when not installed globally (and that look poetry to the
-# untrained eye!)
sys.path.append(
os.path.abspath(
os.path.join(
os.path.dirname(
sys.argv[0]), "..")))
-from ftpcloudfs.server import RackspaceCloudAuthorizer, RackspaceCloudFilesFS
-from ftpcloudfs.constants import version, default_address, default_port
-from ftpcloudfs.monkeypatching import MyDTPHandler
-
-
-def setup_log(log_file=None):
- ''' Setup Logging '''
-
- def log(log_type, msg):
- """
- Dummy function.
- """
- log_type(msg)
- ftpserver.log = lambda msg: log(logging.info, msg)
- ftpserver.logline = lambda msg: log(logging.debug, msg)
- ftpserver.logerror = lambda msg: log(logging.error, msg)
-
- log_format = '%(asctime)-15s - %(levelname)s - %(message)s'
- logging.basicConfig(filename=log_file,
- format=log_format,
- level=logging.DEBUG)
-
-
-def main():
- ''' Main function'''
- parser = OptionParser(usage="ftpcloudfs [OPTIONS].....")
- parser.add_option('-p', '--port',
- type="int",
- dest="port",
- default=default_port,
- help="Port to bind the server default: %d." %
- (default_port))
-
- parser.add_option('-b', '--bind-address',
- type="str",
- dest="bind_address",
- default=default_address,
- help="Address to bind default: %s." % (default_address))
-
- parser.add_option('-s', '--service-net',
- action="store_true",
- dest="servicenet",
- default=False,
- help="Connect via Rackspace ServiceNet network.")
-
- parser.add_option('-l', '--log-file',
- type="str",
- dest="log_file",
- default=None,
- help="Log File: Default stdout")
-
- parser.add_option('-a', '--auth-url',
- type="str",
- dest="authurl",
- default=None,
- help="Auth URL for alternate providers (eg OpenStack)")
-
- (options, _) = parser.parse_args()
-
- setup_log(options.log_file)
-
- ftp_handler = ftpserver.FTPHandler
- ftp_handler.dtp_handler = MyDTPHandler
-
- ftp_handler.banner = 'Rackspace Cloud Files %s using %s' % \
- (version, ftp_handler.banner)
- ftp_handler.authorizer = RackspaceCloudAuthorizer()
- ftp_handler.authorizer.servicenet = options.servicenet
- ftp_handler.authorizer.authurl = options.authurl
-
- ftp_handler.abstracted_fs = RackspaceCloudFilesFS
-
- try:
- ftp_handler.masquerade_address = gethostbyname(options.bind_address)
- except gaierror, (_, errmsg):
- sys.exit('Address error: %s' % errmsg)
-
- ftpd = ftpserver.FTPServer((options.bind_address,
- options.port),
- ftp_handler)
- ftpd.serve_forever()
-
+from ftpcloudfs.main import Main
if __name__ == '__main__':
- main()
+ main = Main()
+ main.main()
View
34 debian/init 100644 → 100755
@@ -14,18 +14,23 @@
# PATH should only include /usr/* if it runs after the mountnfs.sh script
PATH=/sbin:/usr/sbin:/bin:/usr/bin
DESC="FTP Cloud-Files"
-NAME=ftp-cloudfs
+NAME="ftp-cloudfs"
DAEMON=/usr/bin/ftpcloudfs
-DAEMON_ARGS="-l /var/log/${NAME}/${NAME}.log"
PIDFILE=/var/run/$NAME.pid
SCRIPTNAME=/etc/init.d/$NAME
+DAEMON_UID=1 #daemon UID
+DAEMON_GID=1 #daemon GID
+LOGDIR=/var/log/${NAME}
+LOGFILE=${LOGDIR}/${NAME}.log
-# Exit if the package is not installed
-[ -x "$DAEMON" ] || exit 0
+DAEMON_ARGS="-l ${LOGFILE} --pid-file=${PIDFILE} --uid=${DAEMON_UID} --gid=${DAEMON_GID}"
# Read configuration variable file if it is present
[ -r /etc/default/$NAME ] && . /etc/default/$NAME
+# Exit if the package is not installed
+[ -x "$DAEMON" ] || exit 0
+
if [ -n "$BIND_ADDRESS" ];then
DAEMON_ARGS="$DAEMON_ARGS -b $BIND_ADDRESS"
fi
@@ -34,6 +39,19 @@ if [ -n "$BIND_PORT" ];then
DAEMON_ARGS="$DAEMON_ARGS -p $BIND_PORT"
fi
+if [ ! -e ${LOGDIR} ]
+then
+ mkdir -p ${LOGDIR}
+ chown ${DAEMON_UID}:${DAEMON_GID} ${LOGDIR}
+fi
+
+if [ ! -e "${LOGFILE}" ]
+then
+ touch "${LOGFILE}"
+ chmod 640 "${LOGFILE}"
+ chown ${DAEMON_UID}:${DAEMON_GID} "${LOGFILE}"
+fi
+
# Load the VERBOSE setting and other rcS variables
. /lib/init/vars.sh
@@ -50,9 +68,13 @@ do_start()
# 0 if daemon has been started
# 1 if daemon was already running
# 2 if daemon could not be started
- start-stop-daemon -c daemon:daemon -u daemon -m --start --quiet --pidfile $PIDFILE --exec $DAEMON --test > /dev/null \
+
+ touch $PIDFILE
+ chown $DAEMON_UID:$DAEMON_GID $PIDFILE
+
+ start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON --test > /dev/null \
|| return 1
- start-stop-daemon -c daemon:daemon -u daemon -m -b --start --quiet --pidfile $PIDFILE --exec $DAEMON -- \
+ start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON -- \
$DAEMON_ARGS \
|| return 2
}
View
@@ -0,0 +1,161 @@
+# -*- encoding: utf-8 -*-
+__author__ = "Chmouel Boudjnah <chmouel@chmouel.com>"
+import sys
+import socket
+import logging
+
+from optparse import OptionParser
+from pyftpdlib import ftpserver
+
+from server import RackspaceCloudAuthorizer, RackspaceCloudFilesFS
+from constants import version, default_address, default_port
+from monkeypatching import MyDTPHandler
+
+
+class Main(object):
+ """ FTPCloudFS: A FTP Proxy Interface to Rackspace Cloud Files or
+ OpenStack swift."""
+
+ def __init__(self):
+ self.options = None
+
+ def setup_log(self):
+ ''' Setup Logging '''
+
+ def log(log_type, msg):
+ """
+ Dummy function.
+ """
+ log_type(msg)
+ ftpserver.log = lambda msg: log(logging.info, msg)
+ ftpserver.logline = lambda msg: log(logging.debug, msg)
+ ftpserver.logerror = lambda msg: log(logging.error, msg)
+
+ log_format = '%(asctime)-15s - %(levelname)s - %(message)s'
+ logging.basicConfig(filename=self.options.log_file,
+ format=log_format,
+ level=logging.DEBUG)
+
+ def parse_arguments(self):
+ ''' Parse Command Line Options '''
+ parser = OptionParser(usage="ftpcloudfs [OPTIONS].....")
+ parser.add_option('-p', '--port',
+ type="int",
+ dest="port",
+ default=default_port,
+ help="Port to bind the server default: %d." % \
+ (default_port))
+
+ parser.add_option('-b', '--bind-address',
+ type="str",
+ dest="bind_address",
+ default=default_address,
+ help="Address to bind by default: %s." % \
+ (default_address))
+
+ parser.add_option('-a', '--auth-url',
+ type="str",
+ dest="authurl",
+ default=None,
+ help="Auth URL for alternate providers" + \
+ "(eg OpenStack)")
+
+ parser.add_option('-s', '--service-net',
+ action="store_true",
+ dest="servicenet",
+ default=False,
+ help="Connect via Rackspace ServiceNet network.")
+
+ parser.add_option('-f', '--foreground',
+ action="store_true",
+ dest="foreground",
+ default=False,
+ help="Do not attempt to daemonize but" + \
+ "run in foreground.")
+
+ parser.add_option('-l', '--log-file',
+ type="str",
+ dest="log_file",
+ default=None,
+ help="Log File: Default stdout when in foreground")
+
+ parser.add_option('--pid-file',
+ type="str",
+ dest="pid_file",
+ default=None,
+ help="Pid file location when in daemon mode.")
+
+ parser.add_option('--uid',
+ type="int",
+ dest="uid",
+ default=None,
+ help="UID to drop the privilige to " + \
+ "when in daemon mode")
+
+ parser.add_option('--gid',
+ type="int",
+ dest="gid",
+ default=None,
+ help="GID to drop the privilige to " + \
+ "when in daemon mode")
+
+ (options, _) = parser.parse_args()
+ self.options = options
+
+ def run_server(self):
+ """Run the main ftp server loop"""
+ ftp_handler = ftpserver.FTPHandler
+ ftp_handler.dtp_handler = MyDTPHandler
+
+ ftp_handler.banner = 'Rackspace Cloud Files %s using %s' % \
+ (version, ftp_handler.banner)
+ ftp_handler.authorizer = RackspaceCloudAuthorizer()
+ ftp_handler.authorizer.servicenet = self.options.servicenet
+ ftp_handler.authorizer.authurl = self.options.authurl
+
+ ftp_handler.abstracted_fs = RackspaceCloudFilesFS
+
+ try:
+ ftp_handler.masquerade_address = \
+ socket.gethostbyname(self.options.bind_address)
+ except socket.gaierror, (_, errmsg):
+ sys.exit('Address error: %s' % errmsg)
+
+ ftpd = ftpserver.FTPServer((self.options.bind_address,
+ self.options.port),
+ ftp_handler)
+
+ ftpd.serve_forever()
+
+ def setup_daemon(self):
+ import daemon
+ from utils import PidFile
+ import tempfile
+
+ daemonContext = daemon.DaemonContext()
+
+ if not self.options.pid_file:
+ self.options.pid_file = "%s/ftpcloudfs" % (tempfile.gettempdir())
+
+ daemonContext.pidfile=PidFile(self.options.pid_file)
+
+ if self.options.uid:
+ daemonContext.uid = self.options.uid
+
+ if self.options.gid:
+ daemonContext.gid = self.options.gid
+
+ return daemonContext
+
+ def main(self):
+ """ Main entry point"""
+ self.parse_arguments()
+ self.setup_log()
+
+ if self.options.foreground:
+ self.run_server()
+ return
+
+ daemonContext = self.setup_daemon()
+ with daemonContext:
+ self.run_server()
View
@@ -1,4 +1,27 @@
import types
+import fcntl
+import os
+
+class PidFile(object):
+ """Context manager that locks a pid file."""
+ def __init__(self, path):
+ self.path = path
+ self.pidfile = None
+
+ def __enter__(self):
+ self.pidfile = open(self.path, "a+")
+ fcntl.flock(self.pidfile.fileno(), fcntl.LOCK_EX | fcntl.LOCK_NB)
+ self.pidfile.seek(0)
+ self.pidfile.truncate()
+ self.pidfile.write(str(os.getpid()))
+ self.pidfile.flush()
+ self.pidfile.seek(0)
+ return self.pidfile
+
+ def __exit__(self, exc_type=None, exc_value=None, exc_tb=None):
+ self.pidfile.close()
+ os.remove(self.path)
+
#from django.utils
def smart_str(s, encoding='utf-8', strings_only=False, errors='strict'):

0 comments on commit 9b39540

Please sign in to comment.