aboutsummaryrefslogtreecommitdiffstats
path: root/tools/xend/lib/console.py
blob: aad60699794937d9ce70453d8bbe3d795bdcfdb1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
#############################################################
## xend/console.py -- Console-management functions for Xend
## Copyright (c) 2004, K A Fraser (University of Cambridge)
#############################################################

import errno, re, os, select, signal, socket, struct, sys


##
## interface:
##  Each control interface owns an instance of this class, which manages
##  the current state of the console interface. Normally a console interface
##  will be one of two state:
##   LISTENING: listening for a connection on TCP port 'self.port'
##   CONNECTED: sending/receiving console data on TCP port 'self.port'
##
##  A dictionary of all active interfaces, indexed by TCP socket descriptor,
##  is accessible as 'interface.interface_list'.
##
##  NB. When a class instance is to be destroyed you *must* call the 'close'
##  method. Otherwise a stale reference will eb left in the interface list.
##
class interface:

    # The various states that a console interface may be in.
    CLOSED    = 0 # No console activity
    LISTENING = 1 # Listening on port 'self.port'. Socket object 'self.sock'.
    CONNECTED = 2 # Active connection on 'self.port'. Socket obj 'self.sock'.


    # Dictionary of all active (non-closed) console interfaces.
    interface_list = {}


    # NB. 'key' is an opaque value that has no meaning in this class.
    def __init__(self, port, key):
        self.status = interface.CLOSED
        self.port   = port
        self.key    = key


    # Is this interface closed (inactive)?
    def closed(self):
        return self.status == interface.CLOSED


    # Is this interface listening?
    def listening(self):
        return self.status == interface.LISTENING


    # Is this interface active and connected?
    def connected(self):
        return self.status == interface.CONNECTED


    # Close the interface, if it is not closed already.
    def close(self):
        if not self.closed():
            del interface.interface_list[self.sock.fileno()]
            self.sock.close()
            del self.sock
            self.status = interface.CLOSED


    # Move the interface into the 'listening' state. Opens a new listening
    # socket and updates 'interface_list'.
    def listen(self):
        # Close old socket (if any), and create a fresh one.
        self.close()
        self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0)

        try:
            # Turn the new socket into a non-blocking listener.
            self.sock.setblocking(False)
            self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
            self.sock.bind(('', self.port))
            self.sock.listen(1)

            # Announce the new status of thsi interface.
            self.status = interface.LISTENING
            interface.interface_list[self.sock.fileno()] = self

        except:
            # In case of trouble ensure we get rid of dangling socket reference
            self.sock.close()
            del self.sock
            raise


    # Move a listening interface into the 'connected' state.
    def connect(self):
        # Pick up a new connection, if one is available.
        try:
            (sock, addr) = self.sock.accept()
        except:
            return 0
        sock.setblocking(False)
        sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

        # Close the listening socket.
        self.sock.close()

        # Publish the new socket and the new interface state.
        self.sock = sock
        self.status = interface.CONNECTED
        interface.interface_list[self.sock.fileno()] = self
        return 1