lars
3e1bfda98e
added help texts for all plugins fixed 'encryption status' bug in 'volume_details' improved rendering of 'volume_properties' and 'user_manager' chmod root directory of mounted volumes to 0777 to avoid later permission problems for samba or WebDAV
266 lines
8.8 KiB
Python
Executable file
266 lines
8.8 KiB
Python
Executable file
#!/usr/bin/env python2.4
|
|
#
|
|
# The daemon script to run the CryptoBox webserver.
|
|
#
|
|
# run the script with "--help" to see all possible paramters
|
|
#
|
|
#
|
|
# Copyright 2006 sense.lab e.V.
|
|
#
|
|
# This file is part of the CryptoBox.
|
|
#
|
|
# The CryptoBox 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.
|
|
#
|
|
# The CryptoBox 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 the CryptoBox; if not, write to the Free Software
|
|
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
#
|
|
|
|
__revision__ = "$Id"
|
|
|
|
import os, sys
|
|
import cryptobox.web.sites
|
|
from cryptobox.core.exceptions import *
|
|
from optparse import OptionParser
|
|
|
|
## check python version
|
|
(ver_major, ver_minor, ver_sub, ver_desc, ver_subsub) = sys.version_info
|
|
if (ver_major < 2) or ((ver_major == 2) and (ver_minor < 4)):
|
|
sys.stderr.write("You need a python version >= 2.4\n")
|
|
sys.stderr.write("Current version is: %s\n" % sys.version)
|
|
sys.exit(1)
|
|
|
|
## check cherrypy dependency
|
|
try:
|
|
import cherrypy
|
|
except:
|
|
sys.stderr.write("Could not import the cherrypy module!\n")
|
|
sys.stderr.write("Try 'apt-get install python-cherrypy'.\n")
|
|
sys.exit(1)
|
|
|
|
## check clearsilver dependency
|
|
try:
|
|
import neo_cgi, neo_util
|
|
except:
|
|
sys.stderr.write("Could not import the clearsilver module!\n")
|
|
sys.stderr.write("Try 'apt-get install python-clearsilver'.\n")
|
|
sys.exit(1)
|
|
|
|
## check configobj dependency
|
|
try:
|
|
import configobj, validate
|
|
except:
|
|
sys.stderr.write("Could not import the configobj or validate module!\n")
|
|
sys.stderr.write("Try 'apt-get install python-configobj'.\n")
|
|
sys.exit(1)
|
|
|
|
|
|
# TODO: change this for the release version [development|production]
|
|
SERVER_ENVIRONMENT = "production"
|
|
|
|
class CryptoBoxWebserver:
|
|
'''this class starts the cherryp webserver and serves the single sites'''
|
|
|
|
def __init__(self, opts):
|
|
self.opts = opts
|
|
## check conffile
|
|
if not os.access(opts.conffile, os.R_OK) or not os.path.isfile(opts.conffile):
|
|
sys.stderr.write("Error: could not read configuration file (%s)\n" % opts.conffile)
|
|
sys.exit(1)
|
|
## store the absolute path as we will chdir later (for daemons)
|
|
self.conffile = os.path.realpath(opts.conffile)
|
|
## initialize site class
|
|
try:
|
|
cherrypy.root = cryptobox.web.sites.WebInterfaceSites(self.conffile)
|
|
except (CBConfigError,CBEnvironmentError), errMsg:
|
|
sys.stderr.write("Error: the CryptoBox is misconfigured - please fix it!\n")
|
|
sys.stderr.write("%s\n" % str(errMsg))
|
|
sys.exit(1)
|
|
#expose static content:
|
|
#I currently have no idea how to cleanly extract the stylesheet path from
|
|
#the config object without an extra cryptobox.core.main.CryptoBoxProps instance.
|
|
#perhaps put config handling into a separate class in CryptoBox.py?
|
|
#
|
|
# the following manual mapping is necessary, as we may not use relative
|
|
# paths in the config file
|
|
cherrypy.config.update({
|
|
"global": {
|
|
"server.socket_port" : int(opts.port),
|
|
"server.socket_host" : opts.host,
|
|
"server.log_to_screen" : not opts.background and opts.verbose,
|
|
"server.log_tracebacks" : opts.verbose,
|
|
"server.environment": SERVER_ENVIRONMENT,
|
|
"server.log_file" : opts.logfile },
|
|
"/cryptobox-misc": {
|
|
"staticFilter.on" : True,
|
|
"staticFilter.dir": os.path.realpath(opts.datadir)},
|
|
"/favicon.ico": {
|
|
"staticFilter.on" : True,
|
|
"staticFilter.file": os.path.realpath(os.path.join(opts.datadir, 'favicon.ico'))}
|
|
|
|
})
|
|
self.define_exit_handlers(cherrypy.root)
|
|
|
|
|
|
def define_exit_handlers(self, cbw):
|
|
import atexit
|
|
import signal
|
|
## define exit handler for normal termination (via sys.exit)
|
|
def exit_handler():
|
|
cbw.cleanup()
|
|
try:
|
|
os.remove(self.opts.pidfile)
|
|
except OSError:
|
|
pass
|
|
atexit.register(exit_handler)
|
|
## catch kill signal
|
|
def kill_signal_handler(signum, frame):
|
|
cbw.cbox.log.info("Kill signal handler called: %d" % signum)
|
|
sys.exit(1)
|
|
signal.signal(signal.SIGTERM, kill_signal_handler)
|
|
|
|
|
|
def start(self):
|
|
cherrypy.server.start()
|
|
|
|
|
|
|
|
|
|
def fork_to_background():
|
|
## this is just copy'n'pasted from http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/278731
|
|
## check the original for exhaustive comments
|
|
try:
|
|
pid = os.fork()
|
|
except OSError, errMsg:
|
|
sys.stderr.write("Error: failed to fork cryptobox daemon process!\n")
|
|
sys.stderr.write("%s\n" % errMsg)
|
|
sys.exit(1)
|
|
if pid == 0: # the first child
|
|
os.setsid()
|
|
try:
|
|
pid = os.fork()
|
|
except OSError, errMsg:
|
|
sys.stderr.write("Error: failed to fork second cryptobox daemon process!\n")
|
|
sys.stderr.write("%s\n" % errMsg)
|
|
sys.exit(1)
|
|
if pid == 0: # the second child
|
|
## we do not change the directory - otherwise there seems to be a race condition with the python interpreter loading this script file
|
|
#os.chdir(os.path.sep)
|
|
os.umask(0)
|
|
else:
|
|
os._exit(0)
|
|
else:
|
|
os._exit(0)
|
|
|
|
|
|
def close_open_files():
|
|
"""this is only necessary if we want to go into background
|
|
we will only close stdin, stdout and stderr
|
|
"""
|
|
import resource # Resource usage information.
|
|
## use the following lines to close all open files (including the log file)
|
|
# maxfd = resource.getrlimit(resource.RLIMIT_NOFILE)[1]
|
|
# if (maxfd == resource.RLIM_INFINITY):
|
|
# maxfd = 1024
|
|
maxfd = 2
|
|
for fd in range(0, maxfd):
|
|
try:
|
|
os.close(fd)
|
|
except OSError: # ERROR, fd wasn't open to begin with (ignored)
|
|
pass
|
|
os.open(os.devnull, os.O_RDWR) # standard input (0)
|
|
os.dup2(0, 1) # standard output (1)
|
|
os.dup2(0, 2) # standard error (2)
|
|
|
|
|
|
def write_pid_file(pid_file):
|
|
if os.path.exists(pid_file):
|
|
sys.stderr.write(
|
|
"Warning: pid file (%s) already exists - overwriting ...\n" % pid_file)
|
|
try:
|
|
pidf = open(pid_file,"w")
|
|
pidf.write(str(os.getpid()))
|
|
pidf.close()
|
|
except (IOError, OSError), errMsg:
|
|
sys.stderr.write(
|
|
"Warning: failed to write pid file (%s): %s\n" % (pid_file, errMsg))
|
|
## it is just a warning - no need to break
|
|
|
|
|
|
def parseOptions():
|
|
import cryptobox
|
|
version = "%prog" + cryptobox.__version__
|
|
parser = OptionParser(version=version)
|
|
parser.set_defaults(conffile="/etc/cryptobox-server/cryptobox.conf",
|
|
pidfile="/var/run/cryptobox-server/webserver.pid",
|
|
background=False,
|
|
datadir="/usr/share/cryptobox-server/www-data",
|
|
logfile="/var/log/cryptobox-server/webserver.log",
|
|
port="8080",
|
|
host="",
|
|
verbose=True)
|
|
parser.add_option("-c", "--config", dest="conffile",
|
|
help="read configuration from FILE", metavar="FILE")
|
|
parser.add_option("","--pidfile", dest="pidfile",
|
|
help="write process id to FILE", metavar="FILE")
|
|
parser.add_option("-B","", dest="background", action="store_true",
|
|
help="run webserver in background (as daemon)")
|
|
parser.add_option("-q","", dest="verbose", action="store_false",
|
|
help="output only errors")
|
|
parser.add_option("","--datadir", dest="datadir", metavar="DIR",
|
|
help="set data directory to DIR")
|
|
parser.add_option("-p","--port", dest="port", metavar="PORT",
|
|
help="listen on PORT")
|
|
parser.add_option("-l","--logfile", dest="logfile", metavar="FILE",
|
|
help="write webserver log to FILE")
|
|
parser.add_option("","--host", dest="host", metavar="HOST",
|
|
help="attach to HOST")
|
|
(options, args) = parser.parse_args()
|
|
## we do not expect any remaining arguments
|
|
if len(args) != 0:
|
|
parser.error("unknown argument: %s" % str(args[0]))
|
|
if not ((not os.path.exists(options.logfile) \
|
|
and os.access(os.path.dirname(options.logfile), os.W_OK)) \
|
|
or os.access(options.logfile, os.W_OK)):
|
|
parser.error("could not write to logfile (%s)" % options.logfile)
|
|
if not os.path.isdir(options.datadir) or not os.access(options.datadir,os.X_OK):
|
|
parser.error("could not access the data directory (%s)" % options.datadir)
|
|
try:
|
|
if (int(options.port) < 0) or (int(options.port) > 65535):
|
|
parser.error("invalid port number: %s" % str(options.port))
|
|
except ValueError:
|
|
parser.error("invalid port specified (%s) - it must be a number" % (options.port))
|
|
return options
|
|
|
|
|
|
if __name__ == "__main__":
|
|
## process arguments
|
|
options = parseOptions()
|
|
## set umask to 022 (aka 755) - octal value
|
|
os.umask(022)
|
|
## run the webserver as a daemon process
|
|
if options.background: fork_to_background()
|
|
## write pid file
|
|
write_pid_file(options.pidfile)
|
|
## initialize the webserver class (before forking to get some error messages)
|
|
cbw = CryptoBoxWebserver(options)
|
|
## close open files to allow background execution
|
|
if options.background: close_open_files()
|
|
## start the webserver
|
|
try:
|
|
cbw.start()
|
|
except CBError, errMsg:
|
|
sys.stderr.write("Failed to start the CryptoBox webserver!\n")
|
|
sys.stderr.write("%s\n" % str(errMsg))
|
|
sys.stderr.write("Check the log file for details.\n")
|
|
sys.exit(1)
|
|
sys.exit(0)
|
|
|