@@ -1,6 +1,6 @@ -#!/usr/bin/env python +#!/-usr/bin/env python # -*- coding: utf-8 -*- """ This file is part of the web2py Web Framework Copyrighted by Massimo Di Pierro @@ -17,11 +17,10 @@ import os import socket import signal import math import logging - import newcron import main from fileutils import w2p_pack, read_file, write_file from shell import run, test @@ -110,15 +109,17 @@ width=500, height=300) canvas.pack() root.update() - img = Tkinter.PhotoImage(file='splashlogo.gif') - pnl = Tkinter.Label(canvas, image=img, background='white', bd=0) - pnl.pack(side='top', fill='both', expand='yes') - # Prevent garbage collection of img - pnl.image=img + logo = 'splashlogo.gif' + if os.path.exists(logo): + img = Tkinter.PhotoImage(file=logo) + pnl = Tkinter.Label(canvas, image=img, background='white', bd=0) + pnl.pack(side='top', fill='both', expand='yes') + # Prevent garbage collection of img + pnl.image=img def add_label(text='Change Me', font_size=12, foreground='#195866', height=1): return Tkinter.Label( master=canvas, width=250, @@ -219,11 +220,11 @@ column=0, sticky=sticky) self.password = Tkinter.Entry(self.root, show='*') self.password.bind('', lambda e: self.start()) - self.password.focus_force() + self.password.focus_force() self.password.grid(row=2, column=1, sticky=sticky) # Prepare the canvas self.canvas = Tkinter.Canvas(self.root, width=300, @@ -319,11 +320,11 @@ self.tb.Destroy() except: pass self.root.destroy() - sys.exit() + sys.exit(0) def error(self, message): """ Show error message """ tkMessageBox.showerror('web2py start server', message) @@ -486,10 +487,16 @@ '--ssl_private_key', default='', dest='ssl_private_key', help='file that contains ssl private key') + parser.add_option('--ca-cert', + action='store', + dest='ssl_ca_certificate', + default=None, + help='Use this file containing the CA certificate to validate X509 certificates from clients') + parser.add_option('-d', '--pid_filename', default='httpserver.pid', dest='pid_filename', help='file to store the pid of the server') @@ -617,10 +624,19 @@ '--run', dest='run', metavar='PYTHON_FILE', default='', help=msg) + + msg = 'run scheduled tasks for the specified apps' + msg += '-K app1,app2,app3' + msg += 'requires a scheduler defined in the models' + parser.add_option('-K', + '--scheduler', + dest='scheduler', + default=None, + help=msg) msg = 'run doctests in web2py environment; ' +\ 'TEST_PATH like a/c/f (c,f optional)' parser.add_option('-T', '--test', @@ -701,11 +717,12 @@ action='store_true', default=False, dest='nobanner', help='Do not print header banner') - msg = 'listen on multiple addresses: "ip:port:cert:key;ip2:port2:cert2:key2;..." (:cert:key optional; no spaces)' + + msg = 'listen on multiple addresses: "ip:port:cert:key:ca_cert;ip2:port2:cert2:key2:ca_cert2;..." (:cert:key optional; no spaces)' parser.add_option('--interfaces', action='store', dest='interfaces', default=None, help=msg) @@ -734,18 +751,20 @@ options.nocron = True # don't start cron jobs options.plain = True # cronjobs use a plain shell options.folder = os.path.abspath(options.folder) - # accept --interfaces in the form "ip:port:cert:key;ip2:port2;ip3:port3:cert3:key3" + # accept --interfaces in the form + # "ip:port:cert:key;ip2:port2;ip3:port3:cert3:key3" # (no spaces; optional cert:key indicate SSL) - # if isinstance(options.interfaces, str): - options.interfaces = [interface.split(':') for interface in options.interfaces.split(';')] + options.interfaces = [ + interface.split(':') for interface in options.interfaces.split(';')] for interface in options.interfaces: interface[1] = int(interface[1]) # numeric port - options.interfaces = [tuple(interface) for interface in options.interfaces] + options.interfaces = [ + tuple(interface) for interface in options.interfaces] if options.numthreads is not None and options.minthreads is None: options.minthreads = options.numthreads # legacy if not options.cronjob: @@ -761,11 +780,36 @@ msg = "New installation: unable to create welcome.w2p file" sys.stderr.write(msg) return (options, args) +def start_schedulers(options): + apps = [app.strip() for app in options.scheduler.split(',')] + try: + from multiprocessing import Process + except: + sys.stderr.write('Sorry, -K only supported for python 2.6-2.7\n') + return + processes = [] + code = "from gluon import current; current._scheduler.loop()" + for app in apps: + print 'starting scheduler for "%s"...' % app + args = (app,True,True,None,False,code) + logging.getLogger().setLevel(logging.DEBUG) + p = Process(target=run, args=args) + processes.append(p) + print "Currently running %s scheduler processes" % (len(processes)) + p.start() + print "Processes started" + for p in processes: + try: + p.join() + except KeyboardInterrupt: + p.terminate() + p.join() + def start(cron=True): """ Start server """ # ## get command line arguments @@ -798,14 +842,22 @@ # ## if -T run doctests (no cron) if hasattr(options,'test') and options.test: test(options.test, verbose=options.verbose) return + + # ## if -K + if options.scheduler: + try: + start_schedulers(options) + except KeyboardInterrupt: + pass + return # ## if -S start interactive shell (also no cron) if options.shell: - if options.args!=None: + if not options.args is None: sys.argv[:] = options.args run(options.shell, plain=options.plain, bpython=options.bpython, import_models=options.import_models, startfile=options.run) return @@ -903,10 +955,11 @@ pid_filename=options.pid_filename, log_filename=options.log_filename, profiler_filename=options.profiler_filename, ssl_certificate=options.ssl_certificate, ssl_private_key=options.ssl_private_key, + ssl_ca_certificate=options.ssl_ca_certificate, min_threads=options.minthreads, max_threads=options.maxthreads, server_name=options.server_name, request_queue_size=options.request_queue_size, timeout=options.timeout, @@ -917,6 +970,8 @@ try: server.start() except KeyboardInterrupt: server.stop() logging.shutdown() + +