source: ogClient-Git/src/ogClient.py @ 700aa89

Last change on this file since 700aa89 was 700aa89, checked in by Jose M. Guisado <jguisado@…>, 3 years ago

#1065 Use getattr when retrieving event socket

If ogClient does not run on windows or linux mode, it does not create a
datagram event socket.

If ogClient runs in virtual or live mode this will cause an error when
calling get_event_socket because the class instance has no member
"event_sock".

Use getattr when retrieving the event socket from ogClient class, if
there is no event socket, then return None.

Fixes 2465ef25b741 (Add event datagram socket)

  • Property mode set to 100644
File size: 5.1 KB
Line 
1#
2# Copyright (C) 2020-2021 Soleta Networks <info@soleta.eu>
3#
4# This program is free software: you can redistribute it and/or modify it under
5# the terms of the GNU Affero General Public License as published by the
6# Free Software Foundation; either version 3 of the License, or
7# (at your option) any later version.
8
9import errno
10import select
11import socket
12import time
13import email
14from io import StringIO
15
16from src.restRequest import *
17from src.ogRest import *
18from enum import Enum
19
20class State(Enum):
21        CONNECTING = 0
22        RECEIVING = 1
23        FORCE_DISCONNECTED = 2
24
25class ogClient:
26        OG_PATH = '/opt/opengnsys/'
27
28        def __init__(self, config):
29                self.CONFIG = config
30
31                self.mode = self.CONFIG['opengnsys']['mode']
32                if self.mode not in {'virtual', 'live', 'linux', 'windows'}:
33                        raise ValueError('Mode not supported.')
34                if self.mode in {'linux', 'windows'}:
35                        self.event_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
36                        self.event_sock.setblocking(0)
37                        self.event_sock.bind(('127.0.0.1', 55885))
38
39                if self.CONFIG['samba']['activate']:
40                        assert('user' in self.CONFIG['samba'])
41                        assert('pass' in self.CONFIG['samba'])
42
43                self.ip = self.CONFIG['opengnsys']['ip']
44                self.port = self.CONFIG['opengnsys']['port']
45                self.ogrest = ogRest(self.CONFIG)
46
47        def get_socket(self):
48                return self.sock
49
50        def get_event_socket(self):
51                return getattr(self, 'event_sock', None)
52
53        def get_state(self):
54                return self.state
55
56        def send_event_hint(self, message):
57                try:
58                        event, action, user = message.split(" ")
59                        logging.warning("%s, %s, %s", event, action, user)
60                except:
61                        logging.warning("Error parsing session datagram")
62                        return
63
64                if (event != "session" or
65                    action not in ['start', 'stop'] or
66                    not user):
67                        logging.warning("Invalid value in session datagram: %s", message)
68
69                payload = jsonBody({'event': event, 'action': action, 'user': user})
70                response = restResponse(ogResponses.EARLY_HINTS, payload)
71                self.send(response.get())
72
73        def cleanup(self):
74                self.data = ""
75                self.content_len = 0
76                self.header_len = 0
77                self.trailer = False
78
79        def connect(self):
80                print('connecting...')
81                self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
82                self.sock.setblocking(0)
83                self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)
84                self.sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPIDLE, 60)
85                self.sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPINTVL, 30)
86                self.sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPCNT, 4)
87
88                self.state = State.CONNECTING
89                self.cleanup()
90
91                try:
92                        self.sock.connect((self.ip, self.port))
93                except socket.error as err:
94                        if err.errno == errno.EINPROGRESS:
95                                return
96                        elif err.errno == errno.ECONNREFUSED:
97                                return
98
99        def send(self, msg):
100                self.sock.send(bytes(msg, 'utf-8'))
101                return len(msg)
102
103        def connect2(self):
104                try:
105                        self.sock.connect((self.ip, self.port))
106                except socket.error as err:
107                        if err.errno == errno.EISCONN:
108                                print('connected')
109                                self.state = State.RECEIVING
110                        else:
111                                time.sleep(1)
112                                print('connection refused, retrying...')
113                                self.state = State.CONNECTING
114                                self.sock.close()
115                                self.connect()
116
117        def receive(self):
118                try:
119                        data = self.sock.recv(1024).decode('utf-8')
120                except socket.error as err:
121                        data = ''
122                        print('failed to received ' + str(err))
123
124                if len(data) == 0:
125                        self.sock.close()
126                        self.ogrest.kill_process()
127                        self.connect()
128                        return
129
130                self.data = self.data + data
131                request = restRequest()
132
133                if not self.trailer:
134                        header_len = self.data.find("\r\n\r\n")
135                        if header_len > 0:
136                                # https://stackoverflow.com/questions/4685217/parse-raw-http-headers
137                                request_line, headers_alone = self.data.split('\n', 1)
138                                headers = email.message_from_file(StringIO(headers_alone))
139
140                                if 'Content-Length' in headers.keys():
141                                        self.content_len = int(headers['Content-Length'])
142
143                                self.trailer = True
144                                # Add 4 because self.data.find("\r\n\r\n") does not count
145                                # "\r\n\r\n" for the length
146                                self.header_len = header_len + 4
147
148                if self.trailer and (len(self.data) >= self.content_len + self.header_len):
149                        request.parser(self.data)
150                        self.ogrest.process_request(request, self)
151                        self.cleanup()
152
153        def disconnect(self):
154                self.state = State.FORCE_DISCONNECTED
155                self.sock.shutdown(socket.SHUT_RDWR)
156                self.sock.close()
157
158        def run(self):
159                while 1:
160                        sock = self.get_socket()
161                        event_sock = self.get_event_socket()
162                        state = self.get_state()
163
164                        if state == State.CONNECTING:
165                                readset = [ sock ]
166                                writeset = [ sock ]
167                                exceptset = [ sock ]
168                        elif state == State.FORCE_DISCONNECTED:
169                                return 0
170                        else:
171                                readset = [ sock, event_sock ] if event_sock else [ sock ]
172                                writeset = [ ]
173                                exceptset = [ ]
174
175                        readable, writable, exception = select.select(readset, writeset, exceptset)
176                        if state == State.CONNECTING and sock in writable:
177                                self.connect2()
178                        elif state == State.RECEIVING and sock in readable:
179                                self.receive()
180                        elif state == State.CONNECTING and sock in exception:
181                                self.connect2()
182                        elif state == State.RECEIVING and event_sock in readable:
183                                message = event_sock.recv(4096).decode('utf-8').rstrip()
184                                self.send_event_hint(message)
185                        else:
186                                print('wrong state, not ever happen!' + str(state))
Note: See TracBrowser for help on using the repository browser.