"Fossies" - the Fresh Open Source Software Archive

Member "Tardis-1.2.1/src/Tardis/HttpInterface.py" (9 Jun 2021, 23995 Bytes) of package /linux/privat/Tardis-1.2.1.tar.gz:


The requested HTML page contains a <FORM> tag that is unusable on "Fossies" in "automatic" (rendered) mode so that page is shown as HTML source code syntax highlighting (style: standard) with prefixed line numbers. Alternatively you can here view or download the uninterpreted source code file. For more information about "HttpInterface.py" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 1.1.5_vs_1.2.1.

    1 # vim: set et sw=4 sts=4 fileencoding=utf-8:
    2 #
    3 # Tardis: A Backup System
    4 # Copyright 2013-2020, Eric Koldinger, All Rights Reserved.
    5 # kolding@washington.edu
    6 #
    7 # Redistribution and use in source and binary forms, with or without
    8 # modification, are permitted provided that the following conditions are met:
    9 #
   10 #     * Redistributions of source code must retain the above copyright
   11 #       notice, this list of conditions and the following disclaimer.
   12 #     * Redistributions in binary form must reproduce the above copyright
   13 #       notice, this list of conditions and the following disclaimer in the
   14 #       documentation and/or other materials provided with the distribution.
   15 #     * Neither the name of the copyright holder nor the
   16 #       names of its contributors may be used to endorse or promote products
   17 #       derived from this software without specific prior written permission.
   18 #
   19 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
   20 # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   21 # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   22 # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
   23 # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
   24 # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
   25 # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
   26 # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
   27 # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
   28 # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
   29 # POSSIBILITY OF SUCH DAMAGE.
   30 
   31 
   32 
   33 import os
   34 import os.path
   35 import logging
   36 import logging.handlers
   37 import json
   38 import argparse
   39 import configparser
   40 import zlib
   41 import daemonize
   42 import base64
   43 
   44 from flask import Flask, Response, session, request, url_for, abort, redirect, make_response
   45 from tornado.wsgi import WSGIContainer
   46 from tornado.httpserver import HTTPServer
   47 from tornado.ioloop import IOLoop
   48 
   49 import Tardis
   50 import Tardis.TardisDB as TardisDB
   51 import Tardis.Util as Util
   52 import Tardis.CacheDir as CacheDir
   53 import Tardis.Defaults as Defaults
   54 
   55 basedir     = Defaults.getDefault('TARDIS_DB')
   56 dbname      = Defaults.getDefault('TARDIS_DBNAME')
   57 port        = Defaults.getDefault('TARDIS_REMOTE_PORT')
   58 configName  = Defaults.getDefault('TARDIS_REMOTE_CONFIG')
   59 pidFile     = Defaults.getDefault('TARDIS_REMOTE_PIDFILE')
   60 
   61 configDefaults = {
   62     'Port'              : port,
   63     'Database'          : basedir,
   64     'DBName'            : dbname,
   65     'LogFile'           : '',
   66     'LogExceptions'     : str(False),
   67     'Verbose'           : '0',
   68     'Daemon'            : str(False),
   69     'User'              : '',
   70     'Group'             : '',
   71     'SSL'               : str(False),
   72     'CertFile'          : '',
   73     'KeyFile'           : '',
   74     'PidFile'           : pidFile,
   75     'Compress'          : str(True),
   76     'AllowCache'        : str(True),
   77     'AllowSchemaUpgrades': str(False)
   78 }
   79 
   80 app = Flask(__name__)
   81 app.secret_key = os.urandom(24)
   82 
   83 dbs = {}
   84 caches= {}
   85 
   86 allowCompress = False
   87 allowCache = False
   88 
   89 args = None
   90 config = None
   91 
   92 logger = None
   93 
   94 def getDB():
   95     if 'host' not in session:
   96         abort(401)
   97     host = session['host']
   98     db = dbs[host]
   99     return db
  100 
  101 def makeDict(row):
  102     if row:
  103         d = {}
  104         for i in list(row.keys()):
  105             d[i] = row[i]
  106         return d
  107     return None
  108 
  109 def compressMsg(string, threshold=1024):
  110     if len(string) > threshold:
  111         comp = zlib.compress(bytes(string, 'utf8'))
  112         if len(comp) < len(string):
  113             app.logger.debug("Compressed %d to %d", len(string), len(comp))
  114             return (comp, True)
  115     return (string, False)
  116 
  117 def createResponse(string, compress=True, cacheable=True, dumps=True):
  118     if dumps:
  119         string = json.dumps(string)
  120 
  121     if compress and allowCompress and len(string) > 1024:
  122         app.logger.debug("Attempting to compress: %d", len(string))
  123         (data, compressed) = compressMsg(string)
  124         response = make_response(data)
  125         if compressed:
  126             response.headers['Content-Encoding'] = 'deflate'
  127     else:
  128         response = make_response(string)
  129 
  130     if cacheable and allowCache:
  131         response.headers['Cache-Control'] = 'max-age=300'
  132     app.logger.debug("Response: %s", str(response.headers))
  133     return response
  134 
  135 @app.errorhandler(TardisDB.NotAuthenticated)
  136 def handleNotAuthenticated(error):
  137     app.logger.info("Not Authenticated Exception: %s", str(error))
  138     response = make_response(str(error))
  139     response.status_code = 401
  140     return response
  141 
  142 @app.errorhandler(TardisDB.AuthenticationFailed)
  143 def handleAuthenticationFailed(error):
  144     app.logger.info("Authentication failed.  Wrong password")
  145     response = make_response(str(error))
  146     response.status_code = 401
  147     return response
  148 
  149 @app.route('/login', methods=['GET', 'POST'])
  150 def login():
  151     if request.method == 'POST':
  152         try:
  153             #app.logger.debug(str(request))
  154             host    = request.form['host']
  155             dbPath  = os.path.join(args.database, host, dbname)
  156             cache   = CacheDir.CacheDir(os.path.join(args.database, host), create=False)
  157             upgrade = config.getboolean('Remote', 'AllowSchemaUpgrades')
  158             tardis  = TardisDB.TardisDB(dbPath, allow_upgrade=upgrade)
  159 
  160             #session['tardis']   = tardis
  161             session['host']     = host
  162             #app.logger.debug(str(session))
  163             dbs[host] = tardis
  164             caches[host] = cache
  165             if tardis.needsAuthentication():
  166                 status = 'AUTH'
  167             else:
  168                 status = 'OK'
  169             return createResponse({"status": status }, compress=False, cacheable=False)
  170         except Exception as e:
  171             app.logger.exception(e)
  172             abort(401)
  173     else:
  174         return '''
  175         <form action="" method="post">
  176             <p><input type=text name=host>
  177             <p><input type=text name=token>
  178             <p><input type=submit value=Login>
  179         </form>
  180         '''
  181 
  182 @app.route('/needsAuthentication')
  183 def needsAuthentication():
  184     db = getDB()
  185     resp = db.needsAuthentication()
  186     return createResponse(resp, compress=False, cacheable=True)
  187 
  188 @app.route('/authenticate1', methods=['POST'])
  189 def authenticate1():
  190     db = getDB()
  191     data = request.form
  192     app.logger.debug("Authenticate 1: Got data: " + str(data))
  193     srpUname = base64.b64decode(data['srpUname'])
  194     srpValueA = base64.b64decode(data['srpValueA'])
  195     srpValueS, srpValueB = db.authenticate1(srpUname, srpValueA)
  196     resp = { "srpValueS": str(base64.b64encode(srpValueS), 'utf8'), "srpValueB": str(base64.b64encode(srpValueB), 'utf8') }
  197     return createResponse(resp, compress=False, cacheable=False)
  198 
  199 @app.route('/authenticate2', methods=['POST'])
  200 def authenticate2():
  201     db = getDB()
  202     data = request.form
  203     app.logger.debug("Authenticate 2: Got data: " + str(data))
  204     srpValueM = base64.b64decode(data['srpValueM'])
  205     srpValueH = db.authenticate2(srpValueM)
  206     resp = { "srpValueH": str(base64.b64encode(srpValueH), 'utf8') }
  207     return createResponse(resp, compress=False, cacheable=False)
  208 
  209 @app.route('/close')
  210 def close():
  211     # remove the username from the session if it's there
  212     #app.logger.info("close Invoked")
  213     host = session.pop('host', None)
  214     if host in dbs:
  215         dbs[host].close()
  216         del dbs[host]
  217     if host in caches:
  218         del caches[host]
  219     ret = { 'status': 'OK' }
  220     return createResponse(ret)
  221 
  222 # getBackupSetInfo
  223 @app.route('/getBackupSetInfo/<name>')
  224 def getBackupSetInfo(name):
  225     #app.logger.info("getBackupSetInfo Invoked: %s", name)
  226     db = getDB()
  227     return createResponse(makeDict(db.getBackupSetInfo(name)))
  228 
  229 @app.route('/getBackupSetInfoById/<int:backupset>')
  230 def getBackupSetInfoById(backupset):
  231     #app.logger.info("getBackupSetInfoById Invoked: %s", backupset)
  232     db = getDB()
  233     return createResponse(makeDict(db.getBackupSetInfoById(backupset)))
  234 
  235 @app.route('/getBackupSetDetails/<backupset>')
  236 def getBackupSetDetails(backupset):
  237     db = getDB()
  238     return createResponse(db.getBackupSetDetails(backupset))
  239 
  240 # lastBackupSet
  241 @app.route('/lastBackupSet/<int:completed>')
  242 def lastBackupSet(completed):
  243     db = getDB()
  244     return createResponse(makeDict(db.lastBackupSet(bool(completed))))
  245 
  246 # listBackupSets
  247 @app.route('/listBackupSets')
  248 def listBackupSets():
  249     #app.logger.info("listBackupSets Invoked")
  250     db = getDB()
  251     sets = []
  252     for backup in db.listBackupSets():
  253         #app.logger.debug(str(backup))
  254         sets.append(makeDict(backup))
  255 
  256     #app.logger.debug(str(sets))
  257     return createResponse(sets)
  258 
  259 @app.route('/getFileInfoByPath/<int:backupset>')
  260 def getFileInfoByPathRoot(backupset):
  261     return getFileInfoByPath(backupset, "/")
  262 
  263 # getFileInfoByPath
  264 @app.route('/getFileInfoByPath/<int:backupset>/<path:pathname>')
  265 def getFileInfoByPath(backupset, pathname):
  266     #app.logger.info("getFileInfoByPath Invoked: %d %s", backupset, pathname)
  267     db = getDB()
  268     return createResponse(makeDict(db.getFileInfoByPath(str(pathname), backupset)))
  269 
  270 @app.route('/getFileInfoForPath/<int:backupset>/<path:pathname>')
  271 def getFileInfoForPath(backupset, pathname):
  272     db = getDB()
  273     pathinfo = []
  274     for i in db.getFileInfoForPath(pathname, backupset):
  275         pathinfo.append(makeDict(i))
  276     return createResponse(pathinfo)
  277 
  278 
  279 @app.route('/getFileInfoByPathForRange/<int:first>/<int:last>/<path:pathname>')
  280 def getFileInfoByPathForRange(first, last, pathname):
  281     db = getDB()
  282     fInfos = []
  283     #return createResponse(json.dumps(makeDict(db.getFileInfoByPathForRange(str(pathname), first, last))))
  284     for (bset, info) in db.getFileInfoByPathForRange(str(pathname), first, last):
  285         fInfos.append((bset, makeDict(info)))
  286     return createResponse(fInfos)
  287 
  288 # getFileInfoByName
  289 @app.route('/getFileInfoByName/<int:backupset>/<int:device>/<int:inode>/<name>')
  290 def getFileInfoByName(backupset, device, inode, name):
  291     #app.logger.info("getFileInfoByName Invoked: %d (%d,%d) %s", backupset, inode, device, name)
  292     db = getDB()
  293     return createResponse(makeDict(db.getFileInfoByName(name, (inode, device), backupset)))
  294 
  295 
  296 # getFileInfoByInode
  297 @app.route('/getFileInfoByInode/<int:backupset>/<int:device>/<int:inode>')
  298 def getFileInfoByInode(backupset, device, inode):
  299     #app.logger.info("getFileInfoByName Invoked: %d (%d,%d) %s", backupset, inode, device)
  300     db = getDB()
  301     return createResponse(makeDict(db.getFileInfoByInode((inode, device), backupset)))
  302 
  303 @app.route('/getFileInfoByChecksum/<int:backupset>/<checksum>')
  304 def getFileInfoByChecksum(backupset, checksum):
  305     #app.logger.info("getFileInfoByChceksum Invoked: %d %s", backupset, checksum)
  306     db = getDB()
  307     return createResponse([makeDict(x) for x in db.getFileInfoByChecksum(checksum, backupset)])
  308 
  309 # getNewFiles
  310 @app.route('/getNewFiles/<int:backupset>/<other>')
  311 def getNewFiles(backupset, other):
  312     db = getDB()
  313     files = []
  314     other = True if other == 'True' else False
  315     for x in db.getNewFiles(backupset, other):
  316         files.append(makeDict(x))
  317     return createResponse(files)
  318 
  319 # readDirectory
  320 @app.route('/readDirectory/<int:backupset>/<int:device>/<int:inode>')
  321 def readDirectory(backupset, device, inode):
  322     #app.logger.info("readDirectory Invoked: %d (%d,%d)", backupset, inode, device)
  323     db = getDB()
  324     directory = []
  325     for x in db.readDirectory((inode, device), backupset):
  326         directory.append(makeDict(x))
  327     return createResponse(directory)
  328 
  329 @app.route('/readDirectoryForRange/<int:device>/<int:inode>/<int:first>/<int:last>')
  330 def readDirectoryForRange(device, inode, first, last):
  331     #app.logger.info("readDirectoryForRange Invoked: %d (%d,%d) %d %d", inode, device, first, last)
  332     db = getDB()
  333     directory = []
  334     for x in db.readDirectoryForRange((inode, device), first, last):
  335         directory.append(makeDict(x))
  336     return createResponse(directory)
  337 
  338 # getChecksumByPath
  339 @app.route('/getChecksumByPath/<int:backupset>/<path:pathname>')
  340 def getChecksumByPath(backupset, pathname):
  341     #app.logger.info("getChecksumByPath Invoked: %d %s", backupset, pathname)
  342     db = getDB()
  343     cksum = db.getChecksumByPath(pathname, backupset)
  344     #app.logger.info("Checksum: %s", cksum)
  345     return createResponse(cksum)
  346 
  347 # getChecksumInfo
  348 @app.route('/getChecksumInfo/<checksum>')
  349 def getChecksumInfo(checksum):
  350     #app.logger.info("getChecksumInfo Invoked: %s", checksum)
  351     db = getDB()
  352     return createResponse(makeDict(db.getChecksumInfo(checksum)))
  353 
  354 @app.route('/getChecksumInfoChain/<checksum>')
  355 def getChecksumInfoChain(checksum):
  356     #app.logger.info("getChecksumInfo Invoked: %s", checksum)
  357     db = getDB()
  358     return createResponse(list(map(makeDict, db.getChecksumInfoChain(checksum))))
  359 
  360 @app.route('/getChecksumInfoChainByPath/<int:backupset>/<path:pathname>')
  361 def getChecksumInfoChainByPath(pathname, backupset):
  362     db = getDB()
  363     return createResponse(list(map(makeDict, db.getChecksumInfoChainByPath(pathname, backupset))))
  364 
  365 @app.route('/getBackupSetInfoForTime/<float:time>')
  366 def getBackupSetInfoForTime(time):
  367     #app.logger.info("getBackupSetInfoForTime Invoked: %f", time)
  368     db = getDB()
  369     return createResponse(makeDict(db.getBackupSetInfoForTime(time)))
  370 
  371 # getFirstBackupSet
  372 @app.route('/getFirstBackupSet/<int:backupset>/<path:pathname>')
  373 def getFirstBackupSet(backupset, pathname):
  374     #app.logger.info("getFirstBackupSet Invoked: %d %s", backupset, pathname)
  375     db = getDB()
  376     if not pathname.startswith('/'):
  377         pathname = '/' + pathname
  378     return createResponse(db.getFirstBackupSet(pathname, backupset))
  379 
  380 # getChainLength
  381 @app.route('/getChainLength/<checksum>')
  382 def getChainLength(checksum):
  383     #app.logger.info("getChainLength Invoked: d %s", checksum)
  384     db = getDB()
  385     return createResponse(db.getChainLength(checksum))
  386 
  387 _blocksize = (64 * 1024)
  388 def _stream(f):
  389     try:
  390         f.seek(0)
  391         r = f.read(_blocksize)
  392         while r:
  393             yield r
  394             r = f.read(_blocksize)
  395     except Exception as e:
  396         app.logger.exception(e)
  397     finally:
  398         f.close()
  399 
  400 @app.route('/getFileData/<checksum>')
  401 def getFileData(checksum):
  402     #app.logger.info("getFileData Invoked: %s", checksum)
  403     db = getDB()
  404     host = session['host']
  405     cache = caches[host]
  406     try:
  407         ckinfo = db.getChecksumInfo(checksum)
  408         ckfile = cache.open(checksum, "rb")
  409         #ckfile = os.path.abspath(cache.path(checksum))
  410         #return send_file(ckfile)
  411         resp = Response(_stream(ckfile))
  412         resp.headers['Content-Length'] = ckinfo['disksize']
  413         resp.headers['Content-Type'] = 'application/octet-stream'
  414         return resp
  415     except:
  416         abort(404)
  417 
  418 @app.route('/getConfigValue/<name>')
  419 def getConfigValue(name):
  420     db = getDB()
  421     #app.logger.info("getConfigValue Invoked: %s", name)
  422     return createResponse(db.getConfigValue(name))
  423 
  424 @app.route('/setConfigValue/<name>/<value>')
  425 def setConfigValue(name, value):
  426     db = getDB()
  427     app.logger.info("setConfigValue Invoked: %s %s", name, value)
  428     return createResponse(db.setConfigValue(name, value))
  429 
  430 @app.route('/setPriority/<int:backupset>/<int:priority>')
  431 def setPriority(backupset, priority):
  432     db = getDB()
  433     app.logger.info("setPriority Invoked: %s %s", backupset, priority)
  434     return createResponse(db.setPriority(backupset, priority))
  435 
  436 @app.route('/setBackupSetName/<int:backupset>/<name>/<int:priority>')
  437 def setBackupSetName(backupset, name, priority):
  438     db = getDB()
  439     app.logger.info("setBackupSetName Invoked: %s %s %s", backupset, name, priority)
  440     return createResponse(db.setBackupSetName(name, priority, backupset))
  441 
  442 @app.route('/setKeys', methods=['POST'])
  443 def setKeys():
  444     #app.logger.info("Form: %s", str(request.form))
  445     try:
  446         db = getDB()
  447         salt  = request.form.get('Salt')
  448         vkey  = request.form.get('SrpVKey')
  449         fKey  = request.form.get('FilenameKey')
  450         cKey  = request.form.get('ContentKey')
  451         if not db.setKeys(base64.b64decode(salt), base64.b64decode(vkey), fKey, cKey):
  452             raise Exception("Unable to set keys")
  453         return "OK"
  454     except Exception as e:
  455         app.logger.exception(e)
  456         abort(403)
  457 
  458 @app.route('/setSrpValues', methods=['POST'])
  459 def setSrpValues():
  460     try:
  461         db = getDB()
  462         salt = request.form['salt']
  463         vkey = request.form['vkey']
  464         if not db.setSrpValues(salt, vkey):
  465             raise Exception("Unable to set token")
  466     except Exception:
  467         abort(403)
  468 
  469 @app.route('/listPurgeSets/<int:backupset>/<int:priority>/<float:timestamp>')
  470 def listPurgeSets(backupset, priority, timestamp):
  471     db = getDB()
  472     sets = []
  473     for x in db.listPurgeSets(priority, timestamp, backupset):
  474         sets.append(makeDict(x))
  475     return createResponse(sets)
  476 
  477 @app.route('/listPurgeIncomplete/<int:backupset>/<int:priority>/<float:timestamp>')
  478 def listPurgeIncomplete(backupset, priority, timestamp):
  479     db = getDB()
  480     sets = []
  481     for x in db.listPurgeIncomplete(priority, timestamp, backupset):
  482         sets.append(makeDict(x))
  483     return createResponse(sets)
  484 
  485 @app.route('/purgeSets/<int:backupset>/<int:priority>/<float:timestamp>')
  486 def purgeSets(backupset, priority, timestamp):
  487     db = getDB()
  488     return createResponse(db.purgeSets(priority, timestamp, backupset))
  489 
  490 @app.route('/purgeIncomplete/<int:backupset>/<int:priority>/<float:timestamp>')
  491 def purgeIncomplete(backupset, priority, timestamp):
  492     db = getDB()
  493     return createResponse(db.purgeIncomplete(priority, timestamp, backupset))
  494 
  495 @app.route('/deleteBackupSet/<int:backupset>')
  496 def deleteBackupSet(backupset):
  497     db = getDB()
  498     return createResponse(db.deleteBackupSet(backupset))
  499 
  500 @app.route('/listOrphanChecksums/<int:isfile>')
  501 def listOrphanChecksums(isfile):
  502     db = getDB()
  503     orphans = list(db.listOrphanChecksums(isfile))
  504     return createResponse(orphans)
  505 
  506 @app.route('/deleteOrphanChecksums/<int:isfile>')
  507 def deleteOrphanChecksums(isfile):
  508     db = getDB()
  509     return createResponse(db.deleteOrphanChecksums(isfile))
  510 
  511 @app.route('/removeOrphans')
  512 def removeOrphans():
  513     db = getDB()
  514     host = session['host']
  515     cache = caches[host]
  516     count, size, rounds = Util.removeOrphans(db, cache)
  517     j = {
  518         'count': count,
  519         'size': size,
  520         'rounds': rounds,
  521     }
  522     return createResponse(j)
  523 
  524 def processArgs():
  525     parser = argparse.ArgumentParser(description='Tardis HTTP Data Server', formatter_class=Util.HelpFormatter, add_help=False)
  526 
  527     parser.add_argument('--config',         dest='config', default=configName, help="Location of the configuration file (Default: %(default)s)")
  528     (args, remaining) = parser.parse_known_args()
  529 
  530     t = 'Remote'
  531     config = configparser.ConfigParser(configDefaults, default_section='Tardis')
  532     config.add_section(t)                   # Make it safe for reading other values from.
  533     config.read(args.config)
  534 
  535     parser.add_argument('--port',               dest='port',            default=config.getint(t, 'Port'), type=int, help='Listen on port (Default: %(default)s)')
  536     parser.add_argument('--dbname',             dest='dbname',          default=config.get(t, 'DBName'), help='Use the database name (Default: %(default)s)')
  537     parser.add_argument('--database',           dest='database',        default=config.get(t, 'Database'), help='Database Directory (Default: %(default)s)')
  538     parser.add_argument('--logfile', '-l',      dest='logfile',         default=config.get(t, 'LogFile'), help='Log to file (Default: %(default)s)')
  539 
  540     parser.add_argument('--verbose', '-v',      dest='verbose',         action='count', default=config.getint(t, 'Verbose'), help='Increase the verbosity (may be repeated)')
  541     parser.add_argument('--exceptions',         dest='exceptions',      action=Util.StoreBoolean, default=config.getboolean(t, 'LogExceptions'), help='Log full exception details')
  542 
  543     parser.add_argument('--daemon',             dest='daemon',          action=Util.StoreBoolean, default=config.getboolean(t, 'Daemon'), help='Run as a daemon')
  544     parser.add_argument('--user',               dest='user',            default=config.get(t, 'User'), help='Run daemon as user.  Valid only if --daemon is set')
  545     parser.add_argument('--group',              dest='group',           default=config.get(t, 'Group'), help='Run daemon as group.  Valid only if --daemon is set')
  546     parser.add_argument('--pidfile',            dest='pidfile',         default=config.get(t, 'PidFile'), help='Use this pidfile to indicate running daemon')
  547 
  548     parser.add_argument('--ssl',                dest='ssl',             action=Util.StoreBoolean, default=config.getboolean(t, 'SSL'), help='Use SSL connections')
  549     parser.add_argument('--certfile',           dest='certfile',        default=config.get(t, 'CertFile'), help='Path to certificate file for SSL connections')
  550     parser.add_argument('--keyfile',            dest='keyfile',         default=config.get(t, 'KeyFile'), help='Path to key file for SSL connections')
  551 
  552     parser.add_argument('--compress',           dest='compress',        action=Util.StoreBoolean, default=config.getboolean(t, 'Compress'), help='Compress data going out')
  553     parser.add_argument('--cache',              dest='cache',           action=Util.StoreBoolean, default=config.getboolean(t, 'AllowCache'), help='Allow caching')
  554 
  555     parser.add_argument('--version',            action='version', version='%(prog)s ' + Tardis.__versionstring__,   help='Show the version')
  556     parser.add_argument('--help', '-h',         action='help')
  557 
  558     Util.addGenCompletions(parser)
  559 
  560     args = parser.parse_args(remaining)
  561     return(args, config)
  562 
  563 
  564 def setupLogging():
  565     levels = [logging.WARNING, logging.INFO, logging.DEBUG]
  566     log = logging.getLogger('')
  567 
  568     verbosity = args.verbose
  569     loglevel = levels[verbosity] if verbosity < len(levels) else logging.DEBUG
  570     log.setLevel(loglevel)
  571 
  572     format = logging.Formatter("%(asctime)s %(levelname)s : %(message)s")
  573 
  574     if args.logfile:
  575         handler = logging.handlers.WatchedFileHandler(args.logfile)
  576     elif args.daemon:
  577         handler = logging.handlers.SysLogHandler()
  578     else:
  579         handler = logging.StreamHandler()
  580 
  581     logging.raiseExceptions = False
  582 
  583     handler.setFormatter(format)
  584     log.addHandler(handler)
  585     return log
  586 
  587 def setup():
  588     global args, config, logger, allowCompress, allowCache
  589     logging.basicConfig(level=logging.INFO)
  590     (args, config) = processArgs()
  591     logger = setupLogging()
  592     if args.compress:
  593         allowCompress = True
  594     if args.cache:
  595         allowCache = True
  596 
  597 def main_flask():
  598     setup()
  599     app.run(debug=True, port=int(port))
  600 
  601 def run_server():
  602     sslOptions = None
  603     if args.ssl:
  604         sslOptions = {
  605             "certfile": args.certfile,
  606             "keyfile" : args.keyfile
  607         }
  608 
  609     logger.info("Tornado server starting: %s", Tardis.__versionstring__)
  610 
  611     http_server = HTTPServer(WSGIContainer(app), ssl_options = sslOptions)
  612     http_server.listen(args.port)
  613     IOLoop.instance().start()
  614 
  615 def tornado():
  616     setup()
  617     if args.daemon:
  618         user  = args.user
  619         group = args.group
  620         pidfile = args.pidfile
  621         fds = [h.stream.fileno() for h in logger.handlers if isinstance(h, logging.StreamHandler)]
  622         logger.info("About to daemonize")
  623 
  624         try:
  625             daemon = daemonize.Daemonize(app="tardisremote", pid=pidfile, action=run_server, user=user, group=group, keep_fds=fds)
  626             daemon.start()
  627         except Exception as e:
  628             logger.critical("Caught Exception on Daemonize call: {}".format(e))
  629             if args.exceptions:
  630                 logger.exception(e)
  631     else:
  632         try:
  633             run_server()
  634         except KeyboardInterrupt:
  635             pass
  636         except Exception as e:
  637             logger.critical("Unable to run server: {}".format(e))
  638             if args.exceptions:
  639                 logger.exception(e)
  640 
  641 if __name__ == "__main__":
  642     main_flask()