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