Revision 4af60ef0

b/vncauthproxy/vncauthproxy.py
18 18
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 19
# 02110-1301, USA.
20 20

  
21
DEFAULT_CTRL_SOCKET = "/tmp/vncproxy.sock"
22
DEFAULT_LOG_FILE = "/var/log/vncauthproxy/vncauthproxy.log"
23
DEFAULT_PID_FILE = "/var/run/vncauthproxy/vncauthproxy.pid"
24
DEFAULT_CONNECT_TIMEOUT = 30
21 25

  
22 26
import os
23 27
import sys
24 28
import logging
25 29
import gevent
30
import daemon
31
import daemon.pidlockfile
26 32

  
27 33
import rfb
28 34

  
29 35
from gevent import socket
30
from signal import SIGTERM
36
from signal import SIGINT, SIGTERM
31 37
from gevent import signal
32 38
from gevent.select import select
33 39

  
......
48 54
    """
49 55
    id = 1
50 56

  
51
    def __init__(self, sport, daddr, dport, password, connect_timeout=30):
57
    def __init__(self, logger, sport, daddr, dport, password, connect_timeout):
52 58
        """
59
        @type logger: logging.Logger
60
        @param logger: the logger to use
53 61
        @type sport: int
54 62
        @param sport: source port
55 63
        @type daddr: str
......
70 78
        self.daddr = daddr
71 79
        self.dport = dport
72 80
        self.password = password
73
        self.log = logging
81
        self.log = logger
74 82
        self.server = None
75 83
        self.client = None
76 84
        self.timeout = connect_timeout
......
84 92
        raise gevent.GreenletExit
85 93

  
86 94
    def info(self, msg):
87
        logging.info("[C%d] %s" % (self.id, msg))
95
        self.log.info("[C%d] %s" % (self.id, msg))
88 96

  
89 97
    def debug(self, msg):
90
        logging.debug("[C%d] %s" % (self.id, msg))
98
        self.log.debug("[C%d] %s" % (self.id, msg))
91 99

  
92 100
    def warn(self, msg):
93
        logging.warn("[C%d] %s" % (self.id, msg))
101
        self.log.warn("[C%d] %s" % (self.id, msg))
94 102

  
95 103
    def error(self, msg):
96
        logging.error("[C%d] %s" % (self.id, msg))
104
        self.log.error("[C%d] %s" % (self.id, msg))
97 105

  
98 106
    def critical(self, msg):
99
        logging.critical("[C%d] %s" % (self.id, msg))
107
        self.log.critical("[C%d] %s" % (self.id, msg))
100 108

  
101 109
    def __str__(self):
102 110
        return "VncAuthProxy: %d -> %s:%d" % (self.sport, self.daddr, self.dport)
......
313 321

  
314 322

  
315 323
def fatal_signal_handler(signame):
316
    logging.info("Caught %s, will raise SystemExit" % signame)
324
    logger.info("Caught %s, will raise SystemExit" % signame)
317 325
    raise SystemExit
318 326

  
319 327

  
......
322 330

  
323 331
    parser = OptionParser()
324 332
    parser.add_option("-s", "--socket", dest="ctrl_socket",
325
                      help="UNIX socket path for control connections",
326
                      default="/tmp/vncproxy.sock",
327
                      metavar="PATH")
333
                      default=DEFAULT_CTRL_SOCKET,
334
                      metavar="PATH",
335
                      help="UNIX socket path for control connections (default: %s" %
336
                          DEFAULT_CTRL_SOCKET)
328 337
    parser.add_option("-d", "--debug", action="store_true", dest="debug",
329 338
                      help="Enable debugging information")
330
    parser.add_option("-l", "--log", dest="logfile", default=None,
331
                      help="Write log to FILE instead of stdout",
332
                      metavar="FILE")
339
    parser.add_option("-l", "--log", dest="log_file",
340
                      default=DEFAULT_LOG_FILE,
341
                      metavar="FILE",
342
                      help="Write log to FILE instead of %s" % DEFAULT_LOG_FILE),
343
    parser.add_option('--pid-file', dest="pid_file",
344
                      default=DEFAULT_PID_FILE,
345
                      metavar='PIDFILE',
346
                      help="Save PID to file (default: %s)" %
347
                          DEFAULT_PID_FILE)
333 348
    parser.add_option("-t", "--connect-timeout", dest="connect_timeout",
334
                      default=30, type="int", metavar="SECONDS",
349
                      default=DEFAULT_CONNECT_TIMEOUT, type="int", metavar="SECONDS",
335 350
                      help="How long to listen for clients to forward")
336 351

  
337 352
    (opts, args) = parser.parse_args(sys.argv[1:])
338 353

  
354
    # Create pidfile
355
    pidf = daemon.pidlockfile.TimeoutPIDLockFile(
356
        opts.pid_file, 10)
357
    
358
    # Initialize logger
339 359
    lvl = logging.DEBUG if opts.debug else logging.INFO
340

  
341
    logging.basicConfig(level=lvl, filename=opts.logfile,
342
                        format="%(asctime)s %(levelname)s: %(message)s",
343
                        datefmt="%m/%d/%Y %H:%M:%S")
360
    logger = logging.getLogger("vncauthproxy")
361
    logger.setLevel(lvl)
362
    formatter = logging.Formatter("%(asctime)s vncauthproxy[%(process)d] %(levelname)s: %(message)s",
363
        "%Y-%m-%d %H:%M:%S")
364
    handler = logging.FileHandler(opts.log_file)
365
    handler.setFormatter(formatter)
366
    logger.addHandler(handler)
367

  
368
    # Become a daemon
369
    # Redirecting stdout and stderr to handler.stream to catch
370
    # early errors in the daemonization process [e.g., pidfile creation]
371
    # which will otherwise go to /dev/null.
372
    daemon_context = daemon.DaemonContext(
373
        pidfile=pidf,
374
        umask=0o0022,
375
        stdout=handler.stream,
376
        stderr=handler.stream,
377
        files_preserve=[handler.stream])
378
    daemon_context.open()
379
    logger.info("Became a daemon")
380

  
381
    # A fork() has occured while daemonizing,
382
    # we *must* reinit gevent
383
    gevent.reinit()
344 384

  
345 385
    if os.path.exists(opts.ctrl_socket):
346
        logging.critical("Socket '%s' already exists" % opts.ctrl_socket)
386
        logger.critical("Socket '%s' already exists" % opts.ctrl_socket)
347 387
        sys.exit(1)
348 388

  
349 389
    # TODO: make this tunable? chgrp as well?
......
355 395
    os.umask(old_umask)
356 396

  
357 397
    ctrl.listen(1)
358
    logging.info("Initalized, waiting for control connections at %s" %
398
    logger.info("Initialized, waiting for control connections at %s" %
359 399
                 opts.ctrl_socket)
360 400

  
361
    # Catch SIGTERM to ensure graceful shutdown,
401
    # Catch signals to ensure graceful shutdown,
362 402
    # e.g., to make sure the control socket gets unlink()ed.
363 403
    #
364 404
    # Uses gevent.signal so the handler fires even during
365 405
    # gevent.socket.accept()
406
    gevent.signal(SIGINT, fatal_signal_handler, "SIGINT")
366 407
    gevent.signal(SIGTERM, fatal_signal_handler, "SIGTERM")
367
    
368 408
    while True:
369 409
        try:
370 410
            client, addr = ctrl.accept()
371
        except (KeyboardInterrupt, SystemExit):
411
        except SystemExit:
372 412
            break
373 413

  
374
        logging.info("New control connection")
414
        logger.info("New control connection")
375 415
        line = client.recv(1024).strip()
376 416
        try:
377 417
            # Control message format:
......
382 422
            # connecting to <source_port>, who will subsequently be forwarded
383 423
            # to a VNC server at <destination_address>:<destination_port>
384 424
            sport, daddr, dport, password = line.split(':', 3)
385
            logging.info("New forwarding [%d -> %s:%d]" %
425
            logger.info("New forwarding [%d -> %s:%d]" %
386 426
                         (int(sport), daddr, int(dport)))
387 427
        except:
388
            logging.warn("Malformed request: %s" % line)
428
            logger.warn("Malformed request: %s" % line)
389 429
            client.send("FAILED\n")
390 430
            client.close()
391 431
            continue
392 432

  
393 433
        client.send("OK\n")
394
        VncAuthProxy.spawn(sport, daddr, dport, password, opts.connect_timeout)
434
        VncAuthProxy.spawn(logger, sport, daddr, dport, password, opts.connect_timeout)
395 435
        client.close()
396 436

  
397
    logging.info("Unlinking control socket at %s" %
437
    logger.info("Unlinking control socket at %s" %
398 438
                 opts.ctrl_socket)
399 439
    os.unlink(opts.ctrl_socket)
440
    daemon_context.close()
400 441
    sys.exit(0)

Also available in: Unified diff