OpenSecurity/bin/opensecurity_client_restful_server.py
author Oliver Maurhart <oliver.maurhart@ait.ac.at>
Thu, 22 May 2014 11:00:33 +0200
changeset 167 1e1811fa44bc
parent 164 b6b9dc0ed2ac
child 168 76267df09d71
permissions -rwxr-xr-x
eXtreme Programming session - changes by Mihai
oliver@167
     1
#!/bin/env python
oliver@167
     2
# -*- coding: utf-8 -*-
oliver@167
     3
oliver@167
     4
# ------------------------------------------------------------
oliver@167
     5
# opensecurity_client_restful_server
oliver@167
     6
# 
oliver@167
     7
# the OpenSecurity client RESTful server
oliver@167
     8
#
oliver@167
     9
# Autor: Oliver Maurhart, <oliver.maurhart@ait.ac.at>
oliver@167
    10
#
oliver@167
    11
# Copyright (C) 2013 AIT Austrian Institute of Technology
oliver@167
    12
# AIT Austrian Institute of Technology GmbH
oliver@167
    13
# Donau-City-Strasse 1 | 1220 Vienna | Austria
oliver@167
    14
# http://www.ait.ac.at
oliver@167
    15
#
oliver@167
    16
# This program is free software; you can redistribute it and/or
oliver@167
    17
# modify it under the terms of the GNU General Public License
oliver@167
    18
# as published by the Free Software Foundation version 2.
oliver@167
    19
# 
oliver@167
    20
# This program is distributed in the hope that it will be useful,
oliver@167
    21
# but WITHOUT ANY WARRANTY; without even the implied warranty of
oliver@167
    22
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
oliver@167
    23
# GNU General Public License for more details.
oliver@167
    24
# 
oliver@167
    25
# You should have received a copy of the GNU General Public License
oliver@167
    26
# along with this program; if not, write to the Free Software
oliver@167
    27
# Foundation, Inc., 51 Franklin Street, Fifth Floor, 
oliver@167
    28
# Boston, MA  02110-1301, USA.
oliver@167
    29
# ------------------------------------------------------------
oliver@167
    30
oliver@167
    31
oliver@167
    32
# ------------------------------------------------------------
oliver@167
    33
# imports
oliver@167
    34
oliver@167
    35
import getpass
oliver@167
    36
import glob
oliver@167
    37
import json
oliver@167
    38
import os
oliver@167
    39
import os.path
oliver@167
    40
import pickle
oliver@167
    41
import platform
oliver@167
    42
import socket
oliver@167
    43
import subprocess
oliver@167
    44
import sys
oliver@167
    45
import threading
oliver@167
    46
import time
oliver@167
    47
import urllib
oliver@167
    48
import urllib2
oliver@167
    49
import web
oliver@167
    50
import threading
oliver@167
    51
import time
oliver@167
    52
import string
oliver@167
    53
import win32api
oliver@167
    54
import win32con
oliver@167
    55
oliver@167
    56
from opensecurity_util import logger, setupLogger, OpenSecurityException
oliver@167
    57
if sys.platform == 'win32' or sys.platform == 'cygwin':
oliver@167
    58
    from cygwin import Cygwin
oliver@167
    59
oliver@167
    60
# local
oliver@167
    61
import __init__ as opensecurity
oliver@167
    62
from environment import Environment
oliver@167
    63
oliver@167
    64
oliver@167
    65
# ------------------------------------------------------------
oliver@167
    66
# const
oliver@167
    67
oliver@167
    68
oliver@167
    69
"""All the URLs we know mapping to class handler"""
oliver@167
    70
opensecurity_urls = (
oliver@167
    71
    '/credentials',             'os_credentials',
oliver@167
    72
    '/keyfile',                 'os_keyfile',
oliver@167
    73
    '/log',                     'os_log',
oliver@167
    74
    '/notification',            'os_notification',
oliver@167
    75
    '/password',                'os_password',
oliver@167
    76
    '/netmount',                'os_netmount',
oliver@167
    77
    '/netumount',               'os_netumount',
oliver@167
    78
    '/',                        'os_root'
oliver@167
    79
)
oliver@167
    80
oliver@167
    81
oliver@167
    82
# ------------------------------------------------------------
oliver@167
    83
# vars
oliver@167
    84
oliver@167
    85
oliver@167
    86
"""lock for read/write log file"""
oliver@167
    87
log_file_lock = threading.Lock()
oliver@167
    88
oliver@167
    89
"""timer for the log file bouncer"""
oliver@167
    90
log_file_bouncer = None
oliver@167
    91
oliver@167
    92
oliver@167
    93
"""The REST server object"""
oliver@167
    94
server = None
oliver@167
    95
oliver@167
    96
oliver@167
    97
# ------------------------------------------------------------
oliver@167
    98
# code
oliver@167
    99
oliver@167
   100
oliver@167
   101
class os_credentials:
oliver@167
   102
oliver@167
   103
    """OpenSecurity '/credentials' handler.
oliver@167
   104
    
oliver@167
   105
    This is called on GET /credentials?text=TEXT.
oliver@167
   106
    Ideally this should pop up a user dialog to insert his
oliver@167
   107
    credentials based the given TEXT.
oliver@167
   108
    """
oliver@167
   109
    
oliver@167
   110
    def GET(self):
oliver@167
   111
        
oliver@167
   112
        # pick the arguments
oliver@167
   113
        args = web.input()
oliver@167
   114
        
oliver@167
   115
        # we _need_ a text
oliver@167
   116
        if not "text" in args:
oliver@167
   117
            raise web.badrequest('no text given')
oliver@167
   118
        
oliver@167
   119
        # remember remote ip
oliver@167
   120
        remote_ip = web.ctx.environ['REMOTE_ADDR']
oliver@167
   121
oliver@167
   122
        # create the process which queries the user
oliver@167
   123
        dlg_image = os.path.join(sys.path[0], 'opensecurity_dialog.pyw')
oliver@167
   124
        process_command = [sys.executable, dlg_image, 'credentials', args.text]
oliver@167
   125
        process = subprocess.Popen(process_command, shell = False, stdout = subprocess.PIPE)        
oliver@167
   126
        
oliver@167
   127
        # run process result handling in seprate thread (not to block main one)
oliver@167
   128
        bouncer = ProcessResultBouncer(process, remote_ip, '/credentials')
oliver@167
   129
        bouncer.start()
oliver@167
   130
         
oliver@167
   131
        return 'user queried for credentials'
oliver@167
   132
oliver@167
   133
oliver@167
   134
class os_keyfile:
oliver@167
   135
oliver@167
   136
    """OpenSecurity '/keyfile' handler.
oliver@167
   137
    
oliver@167
   138
    This is called on GET /keyfile?text=TEXT.
oliver@167
   139
    Ideally this should pop up a user dialog to insert his
oliver@167
   140
    password along with a keyfile.
oliver@167
   141
    """
oliver@167
   142
    
oliver@167
   143
    def GET(self):
oliver@167
   144
        
oliver@167
   145
        # pick the arguments
oliver@167
   146
        args = web.input()
oliver@167
   147
        
oliver@167
   148
        # we _need_ a text
oliver@167
   149
        if not "text" in args:
oliver@167
   150
            raise web.badrequest('no text given')
oliver@167
   151
            
oliver@167
   152
        # remember remote ip
oliver@167
   153
        remote_ip = web.ctx.environ['REMOTE_ADDR']
oliver@167
   154
        
oliver@167
   155
        # create the process which queries the user
oliver@167
   156
        dlg_image = os.path.join(sys.path[0], 'opensecurity_dialog.pyw')
oliver@167
   157
        process_command = [sys.executable, dlg_image, 'keyfile', args.text]
oliver@167
   158
        process = subprocess.Popen(process_command, shell = False, stdout = subprocess.PIPE)        
oliver@167
   159
        
oliver@167
   160
        # run process result handling in seprate thread (not to block main one)
oliver@167
   161
        bouncer = ProcessResultBouncer(process, remote_ip, '/keyfile')
oliver@167
   162
        bouncer.start()
oliver@167
   163
         
oliver@167
   164
        return 'user queried for password and keyfile'
oliver@167
   165
oliver@167
   166
oliver@167
   167
class os_log:
oliver@167
   168
oliver@167
   169
    """OpenSecurity '/log' handler.
oliver@167
   170
    
oliver@167
   171
    This is called on GET or POST on the log function /log
oliver@167
   172
    """
oliver@167
   173
    
oliver@167
   174
    def GET(self):
oliver@167
   175
        
oliver@167
   176
        # pick the arguments
oliver@167
   177
        self.POST()
oliver@167
   178
oliver@167
   179
oliver@167
   180
    def POST(self):
oliver@167
   181
        
oliver@167
   182
        # pick the arguments
oliver@167
   183
        args = web.input()
oliver@167
   184
        args['user'] = getpass.getuser()
oliver@167
   185
        args['system'] = platform.node() + " " + platform.system() + " " + platform.release()
oliver@167
   186
oliver@167
   187
        # add these to new data to log
oliver@167
   188
        global log_file_lock
oliver@167
   189
        log_file_name = os.path.join(Environment('OpenSecurity').log_path, 'vm_new.log')
oliver@167
   190
        log_file_lock.acquire()
oliver@167
   191
        pickle.dump(args,  open(log_file_name, 'ab'))
oliver@167
   192
        log_file_lock.release()
oliver@167
   193
oliver@167
   194
        return "Ok"
oliver@167
   195
oliver@167
   196
oliver@167
   197
class os_notification:
oliver@167
   198
oliver@167
   199
    """OpenSecurity '/notification' handler.
oliver@167
   200
    
oliver@167
   201
    This is called on GET /notification?msgtype=TYPE&text=TEXT.
oliver@167
   202
    This will pop up an OpenSecurity notifcation window
oliver@167
   203
    """
oliver@167
   204
    
oliver@167
   205
    def GET(self):
oliver@167
   206
        
oliver@167
   207
        # pick the arguments
oliver@167
   208
        args = web.input()
oliver@167
   209
        
oliver@167
   210
        # we _need_ a type
oliver@167
   211
        if not "msgtype" in args:
oliver@167
   212
            raise web.badrequest('no msgtype given')
oliver@167
   213
            
oliver@167
   214
        if not args.msgtype in ['information', 'warning', 'critical']:
oliver@167
   215
            raise web.badrequest('Unknown value for msgtype')
oliver@167
   216
            
oliver@167
   217
        # we _need_ a text
oliver@167
   218
        if not "text" in args:
oliver@167
   219
            raise web.badrequest('no text given')
oliver@167
   220
            
oliver@167
   221
        # invoke the user dialog as a subprocess
oliver@167
   222
        dlg_image = os.path.join(sys.path[0], 'opensecurity_dialog.py')
oliver@167
   223
        process_command = [sys.executable, dlg_image, 'notification-' + args.msgtype, args.text]
oliver@167
   224
        process = subprocess.Popen(process_command, shell = False, stdout = subprocess.PIPE)
oliver@167
   225
oliver@167
   226
        return "Ok"
oliver@167
   227
oliver@167
   228
oliver@167
   229
class os_password:
oliver@167
   230
oliver@167
   231
    """OpenSecurity '/password' handler.
oliver@167
   232
    
oliver@167
   233
    This is called on GET /password?text=TEXT.
oliver@167
   234
    Ideally this should pop up a user dialog to insert his
oliver@167
   235
    password based device name.
oliver@167
   236
    """
oliver@167
   237
    
oliver@167
   238
    def GET(self):
oliver@167
   239
        
oliver@167
   240
        # pick the arguments
oliver@167
   241
        args = web.input()
oliver@167
   242
        
oliver@167
   243
        # we _need_ a text
oliver@167
   244
        if not "text" in args:
oliver@167
   245
            raise web.badrequest('no text given')
oliver@167
   246
            
oliver@167
   247
        # remember remote ip
oliver@167
   248
        remote_ip = web.ctx.environ['REMOTE_ADDR']
oliver@167
   249
        
oliver@167
   250
        # create the process which queries the user
oliver@167
   251
        dlg_image = os.path.join(sys.path[0], 'opensecurity_dialog.pyw')
oliver@167
   252
        process_command = [sys.executable, dlg_image, 'password', args.text]
oliver@167
   253
        process = subprocess.Popen(process_command, shell = False, stdout = subprocess.PIPE)        
oliver@167
   254
        
oliver@167
   255
        # run process result handling in seprate thread (not to block main one)
oliver@167
   256
        bouncer = ProcessResultBouncer(process, remote_ip, '/password')
oliver@167
   257
        bouncer.start()
oliver@167
   258
        
oliver@167
   259
        return 'user queried for password'
oliver@167
   260
oliver@167
   261
# handles netumount request                    
oliver@167
   262
class MountNetworkDriveHandler(threading.Thread): 
oliver@167
   263
    drive = None
oliver@167
   264
    resource = None
oliver@167
   265
    
oliver@167
   266
    def __init__(self, drv, net_path):
oliver@167
   267
        threading.Thread.__init__(self)
oliver@167
   268
        self.drive = drv
oliver@167
   269
        self.networkPath = net_path
oliver@167
   270
    
oliver@167
   271
    def run(self):
oliver@167
   272
        #Check for drive availability
oliver@167
   273
        if os.path.exists(self.drive):
oliver@167
   274
            logger.error("Drive letter is already in use: " + self.drive)
oliver@167
   275
            return 1
oliver@167
   276
        
oliver@167
   277
        #Check for network resource availability
oliver@167
   278
        retry = 5
oliver@167
   279
        while not os.path.exists(self.networkPath):
oliver@167
   280
            time.sleep(1)
oliver@167
   281
            if retry == 0:
oliver@167
   282
                return 1
oliver@167
   283
            logger.info("Path not accessible: " + self.networkPath + " retrying")
oliver@167
   284
            retry-=1
oliver@167
   285
    
oliver@167
   286
        command = 'USE ' + self.drive + ' ' + self.networkPath + ' /PERSISTENT:NO'
oliver@167
   287
    
oliver@167
   288
        result = Cygwin.checkResult(Cygwin.execute('C:\\Windows\\system32\\NET', command))
oliver@167
   289
        if string.find(result[1], 'successfully',) == -1:
oliver@167
   290
            logger.error("Failed: NET " + command)
oliver@167
   291
            return 1
oliver@167
   292
        return 0
oliver@167
   293
oliver@167
   294
class os_netmount:
oliver@167
   295
    
oliver@167
   296
    """OpenSecurity '/netmount' handler"""
oliver@167
   297
    
oliver@167
   298
    def GET(self):
oliver@167
   299
        # pick the arguments
oliver@167
   300
        args = web.input()
oliver@167
   301
        
oliver@167
   302
        # we _need_ a net_resource
oliver@167
   303
        if not "net_resource" in args:
oliver@167
   304
            raise web.badrequest('no net_resource given')
oliver@167
   305
        
oliver@167
   306
        # we _need_ a drive_letter
oliver@167
   307
        if not "drive_letter" in args:
oliver@167
   308
            raise web.badrequest('no drive_letter given')
oliver@167
   309
oliver@167
   310
        driveHandler = MountNetworkDriveHandler(args['drive_letter'], args['net_resource'])
oliver@167
   311
        driveHandler.start()
oliver@167
   312
        driveHandler.join(None)
oliver@167
   313
        return 'Ok'
oliver@167
   314
oliver@167
   315
         
oliver@167
   316
        
oliver@167
   317
# handles netumount request                    
oliver@167
   318
class UmountNetworkDriveHandler(threading.Thread): 
oliver@167
   319
    drive = None
oliver@167
   320
    running = True
oliver@167
   321
    
oliver@167
   322
    def __init__(self, drv):
oliver@167
   323
        threading.Thread.__init__(self)
oliver@167
   324
        self.drive = drv
oliver@167
   325
oliver@167
   326
    def run(self):
oliver@167
   327
        while self.running:
oliver@167
   328
            result = Cygwin.checkResult(Cygwin.execute('C:\\Windows\\system32\\net.exe', 'USE'))
oliver@167
   329
            mappedDrives = list()
oliver@167
   330
            for line in result[1].splitlines():
oliver@167
   331
                if 'USB' in line or 'Download' in line:
oliver@167
   332
                    parts = line.split()
oliver@167
   333
                    mappedDrives.append(parts[1])
oliver@167
   334
            
oliver@167
   335
            logger.info(mappedDrives)
oliver@167
   336
            logger.info(self.drive)
oliver@167
   337
            if self.drive not in mappedDrives:
oliver@167
   338
                self.running = False
oliver@167
   339
            else:
oliver@167
   340
                command = 'USE ' + self.drive + ' /DELETE /YES' 
oliver@167
   341
                result = Cygwin.checkResult(Cygwin.execute('C:\\Windows\\system32\\net.exe', command)) 
oliver@167
   342
                if string.find(str(result[1]), 'successfully',) == -1:
oliver@167
   343
                    logger.error(result[2])
oliver@167
   344
                    continue
oliver@167
   345
                        
oliver@167
   346
oliver@167
   347
class os_netumount:
oliver@167
   348
    
oliver@167
   349
    """OpenSecurity '/netumount' handler"""
oliver@167
   350
    
oliver@167
   351
    def GET(self):
oliver@167
   352
        # pick the arguments
oliver@167
   353
        args = web.input()
oliver@167
   354
        
oliver@167
   355
        # we _need_ a drive_letter
oliver@167
   356
        if not "drive_letter" in args:
oliver@167
   357
            raise web.badrequest('no drive_letter given')
oliver@167
   358
        
oliver@167
   359
        driveHandler = UmountNetworkDriveHandler(args['drive_letter'])
oliver@167
   360
        driveHandler.start()
oliver@167
   361
        driveHandler.join(None)
oliver@167
   362
        return 'Ok'
oliver@167
   363
    
oliver@167
   364
oliver@167
   365
class os_root:
oliver@167
   366
oliver@167
   367
    """OpenSecurity '/' handler"""
oliver@167
   368
    
oliver@167
   369
    def GET(self):
oliver@167
   370
    
oliver@167
   371
        res = "OpenSecurity-Client RESTFul Server { \"version\": \"%s\" }" % opensecurity.__version__
oliver@167
   372
        
oliver@167
   373
        # add some sample links
oliver@167
   374
        res = res + """
oliver@167
   375
        
oliver@167
   376
USAGE EXAMPLES:
oliver@167
   377
        
oliver@167
   378
Request a password: 
oliver@167
   379
    (copy paste this into your browser's address field after the host:port)
oliver@167
   380
    
oliver@167
   381
    /password?text=Give+me+a+password+for+device+%22My+USB+Drive%22+(ID%3A+32090-AAA-X0)
oliver@167
   382
    
oliver@167
   383
    (eg.: http://127.0.0.1:8090/password?text=Give+me+a+password+for+device+%22My+USB+Drive%22+(ID%3A+32090-AAA-X0))
oliver@167
   384
    NOTE: check yout taskbar, the dialog window may not pop up in front of your browser window.
oliver@167
   385
    
oliver@167
   386
    
oliver@167
   387
Request a combination of user and password:
oliver@167
   388
    (copy paste this into your browser's address field after the host:port)
oliver@167
   389
    
oliver@167
   390
    /credentials?text=Tell+the+NSA+which+credentials+to+use+in+order+to+avoid+hacking+noise+on+wire.
oliver@167
   391
    
oliver@167
   392
    (eg.: http://127.0.0.1:8090/credentials?text=Tell+the+NSA+which+credentials+to+use+in+order+to+avoid+hacking+noise+on+wire.)
oliver@167
   393
    NOTE: check yout taskbar, the dialog window may not pop up in front of your browser window.
oliver@167
   394
    
oliver@167
   395
oliver@167
   396
Request a combination of password and keyfile:
oliver@167
   397
    (copy paste this into your browser's address field after the host:port)
oliver@167
   398
    
oliver@167
   399
    /keyfile?text=Your%20private%20RSA%20Keyfile%3A
oliver@167
   400
    
oliver@167
   401
    (eg.: http://127.0.0.1:8090//keyfile?text=Your%20private%20RSA%20Keyfile%3A)
oliver@167
   402
    NOTE: check yout taskbar, the dialog window may not pop up in front of your browser window.
oliver@167
   403
    
oliver@167
   404
oliver@167
   405
Start a Browser:
oliver@167
   406
    (copy paste this into your browser's address field after the host:port)
oliver@167
   407
oliver@167
   408
    /application?vm=Debian+7&app=Browser
oliver@167
   409
oliver@167
   410
    (e.g. http://127.0.0.1:8090/application?vm=Debian+7&app=Browser)
oliver@167
   411
        """
oliver@167
   412
    
oliver@167
   413
        return res
oliver@167
   414
oliver@167
   415
oliver@167
   416
class ProcessResultBouncer(threading.Thread):
oliver@167
   417
oliver@167
   418
    """A class to post the result of a given process - assuming it to be in JSON - to a REST Api."""
oliver@167
   419
oliver@167
   420
    def __init__(self, process, remote_ip, resource): 
oliver@167
   421
oliver@167
   422
        """ctor"""
oliver@167
   423
oliver@167
   424
        threading.Thread.__init__(self)
oliver@167
   425
        self._process = process
oliver@167
   426
        self._remote_ip = remote_ip
oliver@167
   427
        self._resource = resource
oliver@167
   428
 
oliver@167
   429
    
oliver@167
   430
    def stop(self):
oliver@167
   431
oliver@167
   432
        """stop thread"""
oliver@167
   433
        self.running = False
oliver@167
   434
        
oliver@167
   435
    
oliver@167
   436
    def run(self):
oliver@167
   437
oliver@167
   438
        """run the thread"""
oliver@167
   439
oliver@167
   440
        # invoke the user dialog as a subprocess
oliver@167
   441
        result = self._process.communicate()[0]
oliver@167
   442
        if self._process.returncode != 0:
oliver@167
   443
            print 'user request has been aborted.'
oliver@167
   444
            return
oliver@167
   445
        
oliver@167
   446
        # all ok, tell send request back appropriate destination
oliver@167
   447
        try:
oliver@167
   448
            j = json.loads(result)
oliver@167
   449
        except:
oliver@167
   450
            print 'error in password parsing'
oliver@167
   451
            return
oliver@167
   452
        
oliver@167
   453
        # by provided a 'data' we turn this into a POST statement
oliver@167
   454
        url_addr = 'http://' + self._remote_ip + ':58080' + self._resource
oliver@167
   455
        req = urllib2.Request(url_addr, urllib.urlencode(j))
oliver@167
   456
        try:
oliver@167
   457
            res = urllib2.urlopen(req)
oliver@167
   458
        except:
oliver@167
   459
            print 'failed to contact: ' + url_addr
oliver@167
   460
            return 
oliver@167
   461
oliver@167
   462
oliver@167
   463
class RESTServerThread(threading.Thread):
oliver@167
   464
oliver@167
   465
    """Thread for serving the REST API."""
oliver@167
   466
oliver@167
   467
    def __init__(self, port): 
oliver@167
   468
oliver@167
   469
        """ctor"""
oliver@167
   470
        threading.Thread.__init__(self)
oliver@167
   471
        self._port = port 
oliver@167
   472
    
oliver@167
   473
    def stop(self):
oliver@167
   474
oliver@167
   475
        """stop thread"""
oliver@167
   476
        self.running = False
oliver@167
   477
        
oliver@167
   478
    
oliver@167
   479
    def run(self):
oliver@167
   480
oliver@167
   481
        """run the thread"""
oliver@167
   482
        _serve(self._port)
oliver@167
   483
oliver@167
   484
oliver@167
   485
oliver@167
   486
def is_already_running(port = 8090):
oliver@167
   487
oliver@167
   488
    """check if this is started twice"""
oliver@167
   489
oliver@167
   490
    try:
oliver@167
   491
        s = socket.create_connection(('127.0.0.1', port), 0.5)
oliver@167
   492
    except:
oliver@167
   493
        return False
oliver@167
   494
oliver@167
   495
    return True
oliver@167
   496
oliver@167
   497
oliver@167
   498
def _bounce_vm_logs():
oliver@167
   499
oliver@167
   500
    """grab all logs from the VMs and push them to the log servers"""
oliver@167
   501
oliver@167
   502
    global log_file_lock
oliver@167
   503
oliver@167
   504
    # pick the highest current number
oliver@167
   505
    cur = 0
oliver@167
   506
    for f in glob.iglob(os.path.join(Environment('OpenSecurity').log_path, 'vm_cur.log.*')):
oliver@167
   507
        try:
oliver@167
   508
            n = f.split('.')[-1:][0]
oliver@167
   509
            if cur < int(n):
oliver@167
   510
                cur = int(n)
oliver@167
   511
        except:
oliver@167
   512
            pass
oliver@167
   513
oliver@167
   514
    cur = cur + 1
oliver@167
   515
oliver@167
   516
    # first add new vm logs to our existing one: rename the log file
oliver@167
   517
    log_file_name_new = os.path.join(Environment('OpenSecurity').log_path, 'vm_new.log')
oliver@167
   518
    log_file_name_cur = os.path.join(Environment('OpenSecurity').log_path, 'vm_cur.log.' + str(cur))
oliver@167
   519
    log_file_lock.acquire()
oliver@167
   520
    try:
oliver@167
   521
        os.rename(log_file_name_new, log_file_name_cur)
oliver@167
   522
        print('new log file: ' + log_file_name_cur)
oliver@167
   523
    except:
oliver@167
   524
        pass
oliver@167
   525
    log_file_lock.release()
oliver@167
   526
oliver@167
   527
    # now we have a list of next log files to dump
oliver@167
   528
    log_files = glob.glob(os.path.join(Environment('OpenSecurity').log_path, 'vm_cur.log.*'))
oliver@167
   529
    log_files.sort()
oliver@167
   530
    for log_file in log_files:
oliver@167
   531
oliver@167
   532
        try:
oliver@167
   533
            f = open(log_file, 'rb')
oliver@167
   534
            while True:
oliver@167
   535
                l = pickle.load(f)
oliver@167
   536
                _push_log(l)
oliver@167
   537
oliver@167
   538
        except EOFError:
oliver@167
   539
oliver@167
   540
            try:
oliver@167
   541
                os.remove(log_file)
oliver@167
   542
            except:
oliver@167
   543
                logger.warning('tried to delete log file (pushed to EOF) "' + log_file + '" but failed')
oliver@167
   544
oliver@167
   545
        except:
oliver@167
   546
            logger.warning('encountered error while pushing log file "' + log_file + '"')
oliver@167
   547
            break
oliver@167
   548
oliver@167
   549
    # start bouncer again ...
oliver@167
   550
    global log_file_bouncer
oliver@167
   551
    log_file_bouncer = threading.Timer(5.0, _bounce_vm_logs)
oliver@167
   552
    log_file_bouncer.start()
oliver@167
   553
oliver@167
   554
oliver@167
   555
def _push_log(log):
oliver@167
   556
    """POST a single log to log server
oliver@167
   557
oliver@167
   558
    @param  log     the log POST param
oliver@167
   559
    """
oliver@167
   560
oliver@167
   561
    try:
oliver@167
   562
        key = win32api.RegOpenKey(win32con.HKEY_LOCAL_MACHINE, 'SOFTWARE\OpenSecurity')
oliver@167
   563
        log_server_url = str(win32api.RegQueryValueEx(key, 'LogServerURL')[0])
oliver@167
   564
        win32api.RegCloseKey(key)
oliver@167
   565
    except:
oliver@167
   566
        logger.critical('Cannot open Registry HKEY_LOCAL_MACHINE\SOFTWARE\OpenSecurity and get LogServerURL value')
oliver@167
   567
        raise
oliver@167
   568
oliver@167
   569
    # by provided a 'data' we turn this into a POST statement
oliver@167
   570
    d = urllib.urlencode(log)
oliver@167
   571
    req = urllib2.Request(log_server_url, d)
oliver@167
   572
    urllib2.urlopen(req)
oliver@167
   573
    logger.debug('pushed log to server: ' + str(log_server_url))
oliver@167
   574
oliver@167
   575
oliver@167
   576
def _serve(port):
oliver@167
   577
oliver@167
   578
    """Start the REST server"""
oliver@167
   579
oliver@167
   580
    global server
oliver@167
   581
oliver@167
   582
    # start the VM-log bouncer timer
oliver@167
   583
    global log_file_bouncer
oliver@167
   584
    log_file_bouncer = threading.Timer(5.0, _bounce_vm_logs)
oliver@167
   585
    log_file_bouncer.start()
oliver@167
   586
oliver@167
   587
    # trick the web.py server 
oliver@167
   588
    sys.argv = [__file__, str(port)]
oliver@167
   589
    server = web.application(opensecurity_urls, globals())
oliver@167
   590
    server.run()
oliver@167
   591
oliver@167
   592
oliver@167
   593
def serve(port = 8090, background = False):
oliver@167
   594
oliver@167
   595
    """Start serving the REST Api
oliver@167
   596
    port ... port number to listen on
oliver@167
   597
    background ... cease into background (spawn thread) and return immediately"""
oliver@167
   598
oliver@167
   599
    # start threaded or direct version
oliver@167
   600
    if background == True:
oliver@167
   601
        t = RESTServerThread(port)
oliver@167
   602
        t.start()
oliver@167
   603
    else:
oliver@167
   604
        _serve(port)
oliver@167
   605
oliver@167
   606
def stop():
oliver@167
   607
oliver@167
   608
    """Stop serving the REST Api"""
oliver@167
   609
oliver@167
   610
    global server
oliver@167
   611
    if server is None:
oliver@167
   612
        return
oliver@167
   613
oliver@167
   614
    global log_file_bouncer
oliver@167
   615
    if log_file_bouncer is not None:
oliver@167
   616
        log_file_bouncer.cancel()
oliver@167
   617
oliver@167
   618
    server.stop()
oliver@167
   619
oliver@167
   620
# start
oliver@167
   621
if __name__ == "__main__":
oliver@167
   622
    serve()
oliver@167
   623