#!/usr/bin/python3
# LAVA Box Agent - Copyright 2018 Lucid Networks - Stephen Loeckle

import argparse
import configparser
import inspect
import json
import logging
import netifaces
import os
import pickle
import requests
import shlex
import socketserver
import sqlite3
import struct
import subprocess
import sys
import tkinter
import traceback
from time import time, sleep
from datetime import datetime, timedelta
from lavaboxagentlib import api, boxinfo, browserrestart, cloudcmd, configbuilder, configextension, devicereboot, displaysize, emailscreenshot, getcurrenturl, getmac, gettoken, jobs, hotkeywatcher, maintenance, occam, popup, sendemail, sendlog, stats, systemanalysis, updatedict, uptime
from multiprocessing import Process

database = ('/var/lib/lavaboxagent/lavaboxagent.db')

class LogRecordStreamHandler(socketserver.StreamRequestHandler):
    def handle(self):
        while True:
            chunk = self.connection.recv(4)
            if len(chunk) < 4:
                break
            slen = struct.unpack('>L', chunk)[0]
            chunk = self.connection.recv(slen)
            while len(chunk) < slen:
                chunk = chunk + self.connection.recv(slen - len(chunk))
            obj = self.unPickle(chunk)
            record = logging.makeLogRecord(obj)
            self.handleLogRecord(record)

    def unPickle(self, data):
#        sortedresults = collections.OrderedDict(sorted(pickle.loads(data).items()))
#        for key,val in sortedresults.items():
#            print("{0} = {1}".format(key,val))
        return pickle.loads(data)

    def handleLogRecord(self, record):
        # if a name is specified, we use the named logger rather than the one
        # implied by the record.
        if self.server.logname is not None:
            name = self.server.logname
        else:
            name = record.name
        logger = logging.getLogger(name)
        # N.B. EVERY record gets logged. This is because Logger.handle
        # is normally called AFTER logger-level filtering. If you want
        # to do filtering, do it at the client end to save wasting
        # cycles and network bandwidth!
        logger.handle(record)

class LogRecordSocketReceiver(socketserver.ThreadingTCPServer):
    allow_reuse_address = True
    lavaboxagentloggingport = 9022
    def __init__(self, host='localhost',
                 port=lavaboxagentloggingport,
                 handler=LogRecordStreamHandler):
        socketserver.ThreadingTCPServer.__init__(self, (host, port), handler)
        self.abort = 0
        self.timeout = 1
        self.logname = None

    def serve_until_stopped(self):
        import select
        abort = 0
        while not abort:
            rd, wr, ex = select.select([self.socket.fileno()],
                                       [], [],
                                       self.timeout)
            if rd:
                self.handle_request()
            abort = self.abort
            
def engines(sysinfo):
    gotthefunc = inspect.stack()[0][3]
    if os.path.exists(database):
        conn = sqlite3.connect(database)
        c = conn.cursor()
    else:
        msg = ('Database not found! Halting!')
        sendlog(gotthefunc, msg, 50)
        sys.exit()
    c.execute('UPDATE system SET value = ? WHERE tags = ?', (0, 'shutdown'))
    c.execute('UPDATE system SET value = ? WHERE tags = ?', (0, 'apishutdowncomplete'))
    c.execute('UPDATE system SET value = ? WHERE tags = ?', (0, 'cloudcmdshutdowncomplete'))
    c.execute('UPDATE system SET value = ? WHERE tags = ?', (0, 'systemanalysisshutdowncomplete'))
    c.execute('SELECT value from system WHERE tags = ?', ('maintenance',))
    row = c.fetchone()
    if row[0] == 1:
        c.execute('UPDATE system SET value = ? WHERE tags = ?', (0, 'maintenance'))
    if row[0] == 2:
        subject = 'Permenant Maintenance Mode Notification'
        message = 'Reminder: this box is in permenant maintenance mode.'
        sendemail(sysinfo,subject,message)
    c.execute('UPDATE system SET value = ? WHERE tags = ?', (0, 'urlerrors'))
    c.execute('UPDATE system SET value = ? WHERE tags = ?', (0, 'checkfundamentalsruns'))
    c.execute('UPDATE system SET value = ? WHERE tags = ?', (0, 'screenanalysisruns'))
    c.execute('UPDATE system SET value = ? WHERE tags = ?', (0, 'activeproblemcount'))
    c.execute('UPDATE system SET strvalue = ? WHERE tags = ?', ('', 'activeproblem'))
    c.execute('UPDATE system SET strvalue = ? WHERE tags = ?', ('' ,'remediation'))
    datenow = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
    c.execute('UPDATE system SET strvalue = ? WHERE tags = ?', (datenow, 'startuptime'))
    conn.commit()
    
    logworker = Process(target=logserver)
    logworker.start()

# Setup system environment and variable defaults

    logger = logging.getLogger('main')
    if sysinfo['SYSTEM']['CONFIG']['LOGLEVEL'] == '10':
        logger.setLevel(logging.DEBUG)
    if sysinfo['SYSTEM']['CONFIG']['LOGLEVEL'] == '20':
        logger.setLevel(logging.INFO)
    if sysinfo['SYSTEM']['CONFIG']['LOGLEVEL'] == '30':
        logger.setLevel(logging.WARNING)
    if sysinfo['SYSTEM']['CONFIG']['LOGLEVEL'] == '40':
        logger.setLevel(logging.ERROR)
    if sysinfo['SYSTEM']['CONFIG']['LOGLEVEL'] == '50':
        logger.setLevel(logging.CRITICAL)

    sock = logging.handlers.SocketHandler('localhost', 9022)
    logger.addHandler(sock)

    msg = ('Debug test')
    sendlog(gotthefunc, msg, 10)
    msg = ('Info test')
    sendlog(gotthefunc, msg, 20)
    msg = ('Warning test')
    sendlog(gotthefunc, msg, 30)
    msg = ('Error test')
    sendlog(gotthefunc, msg, 40)
    msg = ('Critical test')
    sendlog(gotthefunc, msg, 50)
    
    if not sysinfo['SYSTEM']['MAC']:
        try:
            sysinfo['SYSTEM']['MAC'] = getmac(sysinfo,'eth0')
        except:
            err = traceback.format_exc()
            msg = ('Unknown Error in {0} Detail: {1}'.format(gotthefunc,err))
            sendlog(gotthefunc, msg, 10)
            print(msg)
            sys.exit()

    if sysinfo['SYSTEM']['CGROUPS'] == True:
        mem = boxinfo('m')
        cgmem = int(mem.total*.9)
        cgcmd = ('{} {} {}'.format('sudo','/usr/bin/lavaboxagent/lavaboxagent-cg',cgmem))
        try:
            subprocess.call(shlex.split(cgcmd))
        except:
            pass
            msg = ("Unexpected error:", sys.exc_info()[0])
            sendlog(gotthefunc, msg, 40)
            err = traceback.format_exc()
            msg = ('Unknown Error in {0} Detail: {1}'.format(gotthefunc,err))
            sendlog(gotthefunc, msg, 10)
        
    if sysinfo['SYSTEM']['CONFIG']['BOXTYPE'] == 'display':
        browserpid = browserrestart(sysinfo)
        c.execute('UPDATE system SET value = ? WHERE tags = ?', (browserpid,'browserpid'))
        conn.commit()
    if sysinfo['SYSTEM']['CONFIG']['BOXTYPE'] == 'occam':
        occampid = occam(sysinfo)
    
#    subject = 'Startup Email'
#    message = 'This is the startup email'
#    sendemail(sysinfo,subject,message)

    apiworker = Process(target=api, args=(sysinfo,))
    apiworker.start()
    sleep(1)
    cloudworker = Process(target=cloudcmd, args=(sysinfo,))
    cloudworker.start()
    sleep(1)
    
    displayconnected = 0

    while displayconnected == 0:
        try:
            displayroot = tkinter.Tk()
        except tkinter.TclError as error:
            msg = ('No Diplay Connected. Waiting 30 Seconds.')
            sendlog(gotthefunc, msg, 40)
            pass
            sleep(30)
        except:
            err = traceback.format_exc()
            msg = ('Unknown Error: {}'.format(err))
            sendlog(gotthefunc, msg, 40)
            pass
            sleep(30)
        else:
            dh,dv = displaysize(displayroot)
            msg = ('Display Connected. Resolution: {} x {}'.format(dh,dv))
            sendlog(gotthefunc, msg, 10)
            displayconnected = 1
            displayroot.destroy()
                        
            xsetcmd = ('{} {} {} {} {}'.format('xset', 'dpms', '0', '0', '0'))
            subprocess.call(shlex.split(xsetcmd))
            xsetcmd = ('{} {} {} {} {} {} {} {}'.format('xset', '-dpms', 's', 'off', 's', 'noblank', 's', 'noexpose'))
            subprocess.call(shlex.split(xsetcmd))
            xsetcmd = ('{} {} {} {}'.format('xset', 's', '0', '0'))
            subprocess.call(shlex.split(xsetcmd))    
    
            systemanalysisworker = Process(target=systemanalysis, args=(sysinfo,))
            systemanalysisworker.start()
            sleep(3)
            jobworker = Process(target=jobs, args=(sysinfo,))
            jobworker.start()
            hotkeyworker = Process(target=hotkeywatcher, args=(sysinfo,))
            hotkeyworker.start()

    logworker.join()
    apiworker.join()
    cloudworker.join()
    if displayconnected == 1:
        systemanalysisworker.join()
        jobworker.join()
        hotkeyworker.join()
        
def logserver():
#    logging.basicConfig(format='%(asctime)s %(processName)-10s %(name)s %(levelname)-8s %(message)s',handlers=[logging.handlers.RotatingFileHandler('/var/log/lavaboxagent/lavaboxagent.log', mode='a', maxBytes=10*1024*1024, backupCount=0, encoding=None, delay=False)])
    logging.basicConfig(format='%(asctime)s %(processName)-10s %(name)s %(levelname)-8s %(message)s',handlers=[logging.handlers.RotatingFileHandler('/var/log/lavaboxagent/lavaboxagent.log', mode='a', maxBytes=10000*1024, backupCount=30, encoding=None, delay=False)])
#    logging.basicConfig(format='%(asctime)s %(processName)-10s %(name)s %(levelname)-8s %(message)s',filename='/var/log/lavaboxagent/lavaboxagent.log')
    tcpserver = LogRecordSocketReceiver()
    tcpserver.serve_until_stopped()

def main(sysinfo):
    gotthefunc = inspect.stack()[0][3]
    msg = ('Entered {} function'.format(gotthefunc))
    tmpdir = '/media/ramdisk'
    

# Setup system defaults

    sysinfo['SYSTEM']['CONFIG'] = {}
    sysinfo['SYSTEM']['CONFIG']['TOKEN'] = None
    sysinfo['SYSTEM']['CONFIG']['BROWSER'] = {}
    sysinfo['SYSTEM']['CONFIG']['NETWORK'] = {}
    sysinfo['SYSTEM']['MAC'] = None
    sysinfo['PLATFORM'] = {}

# Read config file

    config = configparser.ConfigParser()
    try:
        config.read(['/etc/lavaboxagent/lavaboxagent.conf'])
    except configparser.Error as e:
        msg = ('Unable to read configuration file /etc/lavaboxagent/lavaboxagent.conf! Halting!')
        sendlog(gotthefunc, msg, 40)
        sys.exit()
    except:
        msg = ('Unknown Error in {0} Reading Config! Halting!'.format(gotthefunc))
        sendlog(gotthefunc, msg, 40)
        err = traceback.format_exc()
        msg = ('Unknown Error in {0} Detail: {1}'.format(gotthefunc,err))
        print(msg)
        sendlog(gotthefunc, msg, 10)
        sys.exit()
    try:
        sysinfo['SYSTEM']['PLATFORM'] = config.get('system', 'platform')
    except (configparser.NoOptionError, configparser.InterpolationMissingOptionError):
        msg = ('No platform type in configuration file! Defaulting to lava!')
        sendlog(gotthefunc, msg, 40)
        sysinfo['SYSTEM']['PLATFORM'] = 'lava'
    except configparser.Error as e:
        msg = ('Config read entry system platform type error! Defaulting to lava!')
        sendlog(gotthefunc, msg, 40)
        sysinfo['SYSTEM']['PLATFORM'] = 'lava'
    except:
        msg = ('Error in {0} Reading Config! Defaulting to lava!'.format(gotthefunc))
        sendlog(gotthefunc, msg, 40)
        err = traceback.format_exc()
        msg = ('Error Detail: {0}'.format(err))
        sendlog(gotthefunc, msg, 10)
        sysinfo['SYSTEM']['PLATFORM'] = 'lava'
    else:
        msg = ('CONFIG: Loaded platform type')
        sendlog(gotthefunc, msg, 10)
    try:
        sysinfo['PLATFORM']['APIHOST'] = config.get('platform', 'apihost')
    except (configparser.NoOptionError, configparser.InterpolationMissingOptionError):
        msg = ('No apihost in configuration file! Defaulting to lava prod!')
        sendlog(gotthefunc, msg, 40)
        sysinfo['PLATFORM']['APIHOST'] = 'https://api.lavacontrols.com'
    except configparser.Error as e:
        msg = ('Config read entry system apihost type error! Defaulting to lava prod!')
        sendlog(gotthefunc, msg, 40)
        sysinfo['PLATFORM']['APIHOST'] = 'https://api.lavacontrols.com'
    except:
        msg = ('Error in {0} Reading Config! Defaulting to lava prod!'.format(gotthefunc))
        sendlog(gotthefunc, msg, 40)
        err = traceback.format_exc()
        msg = ('Error Detail: {0}'.format(err))
        sendlog(gotthefunc, msg, 10)
        sysinfo['PLATFORM']['APIHOST'] = 'https://api.lavacontrols.com'
    else:
        msg = ('CONFIG: Loaded apihost')
        sendlog(gotthefunc, msg, 10)
        
    try:
        sysinfo['SYSTEM']['APPHOST'] = config.get('system', 'apphost')
    except (configparser.NoOptionError, configparser.InterpolationMissingOptionError):
        msg = ('No apphost in configuration file! Defaulting to lava prod!')
        sendlog(gotthefunc, msg, 40)
        sysinfo['SYSTEM']['APPHOST'] = 'https://app.lavacontrols.com'
    except configparser.Error as e:
        msg = ('Config read entry system apphost type error! Defaulting to lava prod!')
        sendlog(gotthefunc, msg, 40)
        sysinfo['SYSTEM']['APPHOST'] = 'https://app.lavacontrols.com'
    except:
        msg = ('Error in {0} Reading Config! Defaulting to lava prod!'.format(gotthefunc))
        sendlog(gotthefunc, msg, 40)
        err = traceback.format_exc()
        msg = ('Error Detail: {0}'.format(err))
        sendlog(gotthefunc, msg, 10)
        sysinfo['SYSTEM']['APPHOST'] = 'https://app.lavacontrols.com'
    else:
        msg = ('CONFIG: Loaded apphost')
        sendlog(gotthefunc, msg, 10)
        
    try:
        sysinfo['SYSTEM']['STREAMHOST'] = config.get('system', 'streamhost')
    except (configparser.NoOptionError, configparser.InterpolationMissingOptionError):
        msg = ('No streamhost in configuration file! Defaulting to lava prod!')
        sendlog(gotthefunc, msg, 40)
        sysinfo['SYSTEM']['STREAMHOST'] = 'rtmp://172.30.2.21'
    except configparser.Error as e:
        msg = ('Config read entry system streamhost type error! Defaulting to lava prod!')
        sendlog(gotthefunc, msg, 40)
        sysinfo['SYSTEM']['STREAMHOST'] = 'rtmp://172.30.2.21'
    except:
        msg = ('Error in {0} Reading Config! Defaulting to lava prod!'.format(gotthefunc))
        sendlog(gotthefunc, msg, 40)
        err = traceback.format_exc()
        msg = ('Error Detail: {0}'.format(err))
        sendlog(gotthefunc, msg, 10)
        sysinfo['SYSTEM']['STREAMHOST'] = 'rtmp://172.30.2.21'
    else:
        msg = ('CONFIG: Loaded streamhost')
        sendlog(gotthefunc, msg, 10)
        
    try:
        sysinfo['SYSTEM']['CGROUPS'] = config.get('system', 'cgroups')
    except (configparser.NoOptionError, configparser.InterpolationMissingOptionError):
        msg = ('No cgroups in configuration file! Defaulting to true!')
        sendlog(gotthefunc, msg, 40)
        sysinfo['SYSTEM']['CGROUPS'] = True
    except configparser.Error as e:
        msg = ('Config read entry system cgroups error! Defaulting to true!')
        sendlog(gotthefunc, msg, 40)
        sysinfo['SYSTEM']['CGROUPS'] = True
    except:
        msg = ('Error in {0} Reading Config! Defaulting to cgroups!'.format(gotthefunc))
        sendlog(gotthefunc, msg, 40)
        err = traceback.format_exc()
        msg = ('Error Detail: {0}'.format(err))
        sendlog(gotthefunc, msg, 10)
        sysinfo['SYSTEM']['CGROUPS'] = True
    else:
        msg = ('CONFIG: Loaded cgroups')
        sendlog(gotthefunc, msg, 10)
    try:
        sysinfo['SYSTEM']['SINGLECOLORDETECTION'] = config.get('system', 'singlecolordetection')
    except (configparser.NoOptionError, configparser.InterpolationMissingOptionError):
        msg = ('No singlecolordetection in configuration file! Defaulting to true!')
        sendlog(gotthefunc, msg, 40)
        sysinfo['SYSTEM']['SINGLECOLORDETECTION'] = True
    except configparser.Error as e:
        msg = ('Config read entry system platform type error! Defaulting to lava!')
        sendlog(gotthefunc, msg, 40)
        sysinfo['SYSTEM']['SINGLECOLORDETECTION'] = True
    except:
        msg = ('Error in {0} Reading Config! Defaulting to lava!'.format(gotthefunc))
        sendlog(gotthefunc, msg, 40)
        err = traceback.format_exc()
        msg = ('Error Detail: {0}'.format(err))
        sendlog(gotthefunc, msg, 10)
        sysinfo['SYSTEM']['SINGLECOLORDETECTION'] = True
    else:
        if sysinfo['SYSTEM']['SINGLECOLORDETECTION'] == 0 or sysinfo['SYSTEM']['SINGLECOLORDETECTION'] == '0' or sysinfo['SYSTEM']['SINGLECOLORDETECTION'] == 'false' or sysinfo['SYSTEM']['SINGLECOLORDETECTION'] == 'False' or sysinfo['SYSTEM']['SINGLECOLORDETECTION'] == 'off' or sysinfo['SYSTEM']['SINGLECOLORDETECTION'] == 'Off':
            sysinfo['SYSTEM']['SINGLECOLORDETECTION'] = False
        msg = ('CONFIG: Loaded singlecolordetection')
        sendlog(gotthefunc, msg, 10)
    try:
        sysinfo['SYSTEM']['CONFIG']['EMAIL'] = [e.strip() for e in config.get('system', 'emailnotification').split(',')]
    except (configparser.NoOptionError, configparser.InterpolationMissingOptionError):
        msg = ('No email(s) for notifications specified in configuration file. Continuing with no notifications.')
        sendlog(gotthefunc, msg, 40)
        sysinfo['SYSTEM']['CONFIG']['EMAIL'] = None
        pass
    except configparser.Error as e:
        msg = ('Config read entry system email notification error! Clearing entry.')
        sendlog(gotthefunc, msg, 40)
        sysinfo['SYSTEM']['CONFIG']['EMAIL'] = None
        pass
    except:
        msg = ('Unknown Error in {0} Reading Config! Halting!'.format(gotthefunc))
        sendlog(gotthefunc, msg, 40)
        err = traceback.format_exc()
        msg = ('Unknown Error in {0} Detail: {1}'.format(gotthefunc,err))
        print(msg)
        sendlog(gotthefunc, msg, 10)
        sys.exit()
    try:
        sysinfo['SYSTEM']['CONFIG']['SMTPHOST'] = config.get('system', 'smtphost')
    except (configparser.NoOptionError, configparser.InterpolationMissingOptionError):
        msg = ('No smtp hosts for notifications specified in configuration file. Default to localhost.')
        sendlog(gotthefunc, msg, 40)
        sysinfo['SYSTEM']['CONFIG']['SMTPHOST'] = 'localhost'
        pass
    except configparser.Error as e:
        msg = ('Config read entry system email notification error! Clearing entry.')
        sendlog(gotthefunc, msg, 40)
        sysinfo['SYSTEM']['CONFIG']['SMTPHOST'] = 'localhost'
        pass
    except:
        msg = ('Unknown Error in {0} Reading Config! Halting!'.format(gotthefunc))
        sendlog(gotthefunc, msg, 40)
        err = traceback.format_exc()
        msg = ('Unknown Error in {0} Detail: {1}'.format(gotthefunc,err))
        print(msg)
        sendlog(gotthefunc, msg, 10)
        sys.exit()
    try:
        sysinfo['SYSTEM']['CONFIG']['LATENCYHOST'] = config.get('system', 'latencyhost')
    except (configparser.NoOptionError, configparser.InterpolationMissingOptionError):
        msg = ('No latency host specified in configuration file. Default to 172.25.16.1, LAVA connectivity gateway.')
        sendlog(gotthefunc, msg, 40)
        sysinfo['SYSTEM']['CONFIG']['LATENCYHOST'] = '172.25.16.1'
        pass
    except configparser.Error as e:
        msg = ('Config read entry system email notification error! Clearing entry.')
        sendlog(gotthefunc, msg, 40)
        sysinfo['SYSTEM']['CONFIG']['LATENCYHOST'] = '172.25.16.1'
        pass
    except:
        msg = ('Unknown Error in {0} Reading Config! Halting!'.format(gotthefunc))
        sendlog(gotthefunc, msg, 40)
        err = traceback.format_exc()
        msg = ('Unknown Error in {0} Detail: {1}'.format(gotthefunc,err))
        print(msg)
        sendlog(gotthefunc, msg, 10)
        sys.exit()
    try:
        sysinfo['SYSTEM']['CONFIG']['BOXTYPE'] = config.get('system', 'boxtype')
    except (configparser.NoOptionError, configparser.InterpolationMissingOptionError):
        err = traceback.format_exc()
        msg = ('Unknown Error in {0} Detail: {1}'.format(gotthefunc,err))
        print(msg)
    except configparser.Error as e:
        err = traceback.format_exc()
        msg = ('Unknown Error in {0} Detail: {1}'.format(gotthefunc,err))
        print(msg)
    except:
        err = traceback.format_exc()
        msg = ('Unknown Error in {0} Detail: {1}'.format(gotthefunc,err))
        print(msg)
        sys.exit()
    try:
        sysinfo['SYSTEM']['CONFIG']['INTERVAL'] = int(config.get('system', 'interval'))
    except (configparser.NoOptionError, configparser.InterpolationMissingOptionError):
        err = traceback.format_exc()
        msg = ('Unknown Error in {0} Detail: {1}'.format(gotthefunc,err))
        print(msg)
        sysinfo['SYSTEM']['CONFIG']['INTERVAL'] = 1
    except configparser.Error as e:
        err = traceback.format_exc()
        msg = ('Unknown Error in {0} Detail: {1}'.format(gotthefunc,err))
        print(msg)
        sysinfo['SYSTEM']['CONFIG']['INTERVAL'] = 1
    except:
        err = traceback.format_exc()
        msg = ('Unknown Error in {0} Detail: {1}'.format(gotthefunc,err))
        print(msg)
        sysinfo['SYSTEM']['CONFIG']['INTERVAL'] = 1
    try:
        sysinfo['SYSTEM']['CONFIG']['STARTUPSECS'] = int(config.get('system', 'startupsecs'))
    except (configparser.NoOptionError, configparser.InterpolationMissingOptionError):
        sysinfo['SYSTEM']['CONFIG']['STARTUPSECS'] = 60
    except configparser.Error as e:
        err = traceback.format_exc()
        msg = ('Unknown Error in {0} Detail: {1}'.format(gotthefunc,err))
        print(msg)
        sysinfo['SYSTEM']['CONFIG']['STARTUPSECS'] = 60
    except:
        err = traceback.format_exc()
        msg = ('Unknown Error in {0} Detail: {1}'.format(gotthefunc,err))
        print(msg)
        sysinfo['SYSTEM']['CONFIG']['STARTUPSECS'] = 60
    try:
        sysinfo['SYSTEM']['CONFIG']['SYSTEMANALYSISSECS'] = int(config.get('system', 'systemanalysissecs'))
    except (configparser.NoOptionError, configparser.InterpolationMissingOptionError):
        sysinfo['SYSTEM']['CONFIG']['SYSTEMANALYSISSECS'] = 30
    except configparser.Error as e:
        err = traceback.format_exc()
        msg = ('Unknown Error in {0} Detail: {1}'.format(gotthefunc,err))
        print(msg)
        sysinfo['SYSTEM']['CONFIG']['SYSTEMANALYSISSECS'] = 30
    except:
        err = traceback.format_exc()
        msg = ('Unknown Error in {0} Detail: {1}'.format(gotthefunc,err))
        print(msg)
        sysinfo['SYSTEM']['CONFIG']['SYSTEMANALYSISSECS'] = 30
    try:
        sysinfo['SYSTEM']['CONFIG']['SYSTEMANALYSISPROBLEMSECS'] = int(config.get('system', 'systemanalysisproblemsecs'))
    except (configparser.NoOptionError, configparser.InterpolationMissingOptionError):
        sysinfo['SYSTEM']['CONFIG']['SYSTEMANALYSISPROBLEMSECS'] = 15
    except configparser.Error as e:
        err = traceback.format_exc()
        msg = ('Unknown Error in {0} Detail: {1}'.format(gotthefunc,err))
        print(msg)
        sysinfo['SYSTEM']['CONFIG']['SYSTEMANALYSISPROBLEMSECS'] = 15
    except:
        err = traceback.format_exc()
        msg = ('Unknown Error in {0} Detail: {1}'.format(gotthefunc,err))
        print(msg)
        sysinfo['SYSTEM']['CONFIG']['SYSTEMANALYSISPROBLEMSECS'] = 15
    try:
        sysinfo['SYSTEM']['CONFIG']['USER'] = config.get('system', 'user')
    except (configparser.NoOptionError, configparser.InterpolationMissingOptionError):
        msg = ('No run-as user defined in configuration file! Defaulting to kiosk!')
        sendlog(gotthefunc, msg, 40)
        sysinfo['SYSTEM']['CONFIG']['USER'] = 'kiosk'
    except configparser.Error as e:
        msg = ('Config read entry system user error! Halting!')
        sendlog(gotthefunc, msg, 40)
        sys.exit()
    except:
        msg = ('Unknown Error in {0} Reading Config! Halting!'.format(gotthefunc))
        sendlog(gotthefunc, msg, 40)
        err = traceback.format_exc()
        msg = ('Unknown Error in {0} Detail: {1}'.format(gotthefunc,err))
        print(msg)
        sendlog(gotthefunc, msg, 10)
        sys.exit()
    try:
        sysinfo['SYSTEM']['CONFIG']['LOGLEVEL'] = config.get('system', 'loglevel')
    except (configparser.NoOptionError, configparser.InterpolationMissingOptionError):
        msg = ('No logging type specified in configuration file. Defaulting to INFO!')
        sendlog(gotthefunc, msg, 40)
        sysinfo['SYSTEM']['LOGLEVEL'] = '10'
        pass
    except configparser.Error as e:
        msg = ('Config read entry system loglevel error! Halting!')
        sendlog(gotthefunc, msg, 40)
        sys.exit()
    except:
        msg = ('Unknown Error in {0} Reading Config! Halting!'.format(gotthefunc))
        sendlog(gotthefunc, msg, 40)
        err = traceback.format_exc()
        msg = ('Unknown Error in {0} Detail: {1}'.format(gotthefunc,err))
        print(msg)
        sendlog(gotthefunc, msg, 10)
        sys.exit()
    try:
        sysinfo['SYSTEM']['CONFIG']['BROWSER']['NAME'] = config.get('browser', 'name')
    except (configparser.NoOptionError, configparser.InterpolationMissingOptionError):
        msg = ('No browser name specified. Defaulting to {}.'.format(sysinfo['SYSTEM']['CONFIG']['BROWSER']['NAME']))
        sendlog(gotthefunc, msg, 40)
        pass
    except configparser.Error as e:
        msg = ('Config read entry browser name! Defaulting to {}.'.format(sysinfo['SYSTEM']['CONFIG']['BROWSER']['NAME']))
        sendlog(gotthefunc, msg, 40)
        pass
    except:
        msg = ('Unknown Error in {0} Reading Config! Halting!'.format(gotthefunc))
        sendlog(gotthefunc, msg, 40)
        err = traceback.format_exc()
        msg = ('Unknown Error in {0} Detail: {1}'.format(gotthefunc,err))
        print(msg)
        sendlog(gotthefunc, msg, 10)
        sys.exit()
    try:
        sysinfo['SYSTEM']['CONFIG']['BROWSER']['PATH'] = config.get('browser', 'path')
    except (configparser.NoOptionError, configparser.InterpolationMissingOptionError):
        msg = ('No browser path specified. Defaulting to {}.'.format(sysinfo['SYSTEM']['CONFIG']['BROWSER']['PATH']))
        sendlog(gotthefunc, msg, 40)
        pass
    except configparser.Error as e:
        msg = ('Config read entry browser path! Defaulting to {}.'.format(sysinfo['SYSTEM']['CONFIG']['BROWSER']['PATH']))
        sendlog(gotthefunc, msg, 40)
        pass
    except:
        msg = ('Unknown Error in {0} Reading Config! Halting!'.format(gotthefunc))
        sendlog(gotthefunc, msg, 40)
        err = traceback.format_exc()
        msg = ('Unknown Error in {0} Detail: {1}'.format(gotthefunc,err))
        print(msg)
        sendlog(gotthefunc, msg, 10)
        sys.exit()
    try:
        sysinfo['SYSTEM']['CONFIG']['BROWSER']['OPTIONS'] = config.get('browser', 'options')
    except (configparser.NoOptionError, configparser.InterpolationMissingOptionError):
        msg = ('No browser options specified. Defaulting to {}.'.format(sysinfo['SYSTEM']['CONFIG']['BROWSER']['OPTIONS']))
        sendlog(gotthefunc, msg, 40)
        pass
    except configparser.Error as e:
        msg = ('Config read entry browser options! Defaulting to {}.'.format(sysinfo['SYSTEM']['CONFIG']['BROWSER']['OPTIONS']))
        sendlog(gotthefunc, msg, 40)
        pass
    except:
        msg = ('Unknown Error in {0} Reading Config! Halting!'.format(gotthefunc))
        sendlog(gotthefunc, msg, 40)
        err = traceback.format_exc()
        msg = ('Unknown Error in {0} Detail: {1}'.format(gotthefunc,err))
        print(msg)
        sendlog(gotthefunc, msg, 10)
        sys.exit()
    try:
        sysinfo['SYSTEM']['CONFIG']['NETWORK']['HTTPPROXY'] = config.get('network', 'httpproxy')
    except (configparser.NoOptionError, configparser.InterpolationMissingOptionError):
        msg = ('No network http proxy specified. Defaulting to {}.'.format(sysinfo['SYSTEM']['CONFIG']['NETWORK']['HTTPPROXY']))
        sendlog(gotthefunc, msg, 40)
        pass
    except configparser.Error as e:
        msg = ('Config read entry network options! Defaulting to {}.'.format(sysinfo['SYSTEM']['CONFIG']['NETWORK']['HTTPPROXY']))
        sendlog(gotthefunc, msg, 40)
        pass
    except:
        msg = ('Unknown Error in {0} Reading Config! Halting!'.format(gotthefunc))
        sendlog(gotthefunc, msg, 40)
        err = traceback.format_exc()
        msg = ('Unknown Error in {0} Detail: {1}'.format(gotthefunc,err))
        print(msg)
        sendlog(gotthefunc, msg, 10)
        sys.exit()

    os.environ["DISPLAY"] = ":0"
    


    if sysinfo['SYSTEM']['CONFIG']['NETWORK']['HTTPPROXY']:
        os.environ["http_proxy"] = sysinfo['SYSTEM']['CONFIG']['NETWORK']['HTTPPROXY']
        os.environ["https_proxy"] = sysinfo['SYSTEM']['CONFIG']['NETWORK']['HTTPPROXY']
        
# Parse CLI options
        
    parser = argparse.ArgumentParser(description='LAVA Box Agent System')
    parser.add_argument('-e', '--engines', help = 'Run lavaboxagent client engines', action="store_true")
    parser.add_argument('-r', '--reconfigure', help = 'Reconfigure lavaboxagent', action='store_true')
    parser.add_argument('-s', '--stats', help = 'Show lavaboxagent stats', action='store_true')
    parser.add_argument('-se', '--statsemail', metavar = 'EMAILADDRESS', help = 'Send stats to email address. Email address required. Multiple email addresses can be added, space delimited', nargs='+')
    parser.add_argument('-m', '--maint', help = 'Put system in maintenance mode. Restarts Chrome out of kiosk mode.', action='store_true')
    parser.add_argument('-mon', '--mainton', help = 'Put system in permenant maintenance mode until removed. Restarts Chrome out of kiosk mode.', action='store_true')
    parser.add_argument('-moff', '--maintoff', help = 'Take system out of permenant maintenance mode.', action='store_true')
    parser.add_argument('-es', '--emailscreenshot', metavar = 'EMAILADDRESS', help = 'Send screenshot to email address. Email address required. Multiple email addresses can be added, space delimited.', nargs='+')
    parser.add_argument('-sd', '--shutdown', help = 'Shutdown the system', action='store_true')
    parser.add_argument('-u', '--url', help = 'Get current URL from Chrome', action='store_true')
    
    
    args = parser.parse_args()
    if (not args.engines and not args.reconfigure and not args.stats and not args.maint and not args.emailscreenshot and not args.mainton and not args.maintoff and not args.statsemail and not args.shutdown and not args.url):
        parser.print_help()
    if args.engines:

    
        sleep(1)
        try:
            subprocess.Popen('/usr/lib/notification-daemon/notification-daemon', stdout=subprocess.PIPE, stderr=subprocess.PIPE)
        except:
            subject = 'DBUS Problem Rebooting'
            err = traceback.format_exc()
            msg = ('Unknown Error in {0} Detail: {1}'.format(gotthefunc,err))
            sendemail(sysinfo,subject,msg,ssfile)
            devicereboot()
        while True:
            try:
                popup('LAVA Box Starting','LAVA Box is starting up. Please stand by.',10000)
            except:
                err = traceback.format_exc()
                msg = ('Error in {} Detail: {}'.format(gotthefunc,err))
                sendlog(gotthefunc, msg, 10)
                sleep(2)
                pass
            else:
                break
        try:
            sysinfo['SYSTEM']['CONFIG']['TOKEN'] = config.get('system', 'token')
        except (configparser.NoOptionError, configparser.InterpolationMissingOptionError):
            err = traceback.format_exc()
            msg = ('Error in {0} Detail: {1}'.format(gotthefunc,err))
            sendlog(gotthefunc, msg, 10)
        except configparser.Error as e:
            err = traceback.format_exc()
            msg = ('Error in {0} Detail: {1}'.format(gotthefunc,err))
            sendlog(gotthefunc, msg, 10)
        except:
            err = traceback.format_exc()
            msg = ('Error in {0} Detail: {1}'.format(gotthefunc,err))
            sendlog(gotthefunc, msg, 10)
            sys.exit()
        if not sysinfo['SYSTEM']['CONFIG']['TOKEN']:
            try:
                sysinfo['SYSTEM']['MAC'] = getmac(sysinfo,'eth0')
            except:
                err = traceback.format_exc()
                msg = ('Unknown Error in {0} Detail: {1}'.format(gotthefunc,err))
                sendlog(gotthefunc, msg, 10)
                sendlog(gotthefunc, msg, 10)
                sys.exit()
            notified = 0
            while True:
                try:
                    popup('LAVA Box Self-Provisioning','Please stand by.',3000)
                except:
                    err = traceback.format_exc()
                    msg = ('Error in {} Detail: {}'.format(gotthefunc,err))
                    sendlog(gotthefunc, msg, 10)
                    pass
                try:
                    sysinfo['SYSTEM']['CONFIG']['TOKEN'] = gettoken(sysinfo)
                except:
                    err = traceback.format_exc()
                    msg = ('Error in {0} Detail: {1}'.format(gotthefunc,err))
                    sendlog(gotthefunc, msg, 10)
                    if notified == 0:
                        subject = 'LAVA Box Failed Self-Provisioning'
                        message = 'LAVA Box Failed Self-Provisioning in applying token to lavaboxagent.conf\n {}'.format(msg)
                        print(message)
                        sendemail(sysinfo,subject,message)
                        notified = 1
                    try:
                        popup('LAVA Box Failed Self-Provisioning','Please contact support@lavacontrols.com!',30000)
                    except:
                        err = traceback.format_exc()
                        msg = ('Error in {} Detail: {}'.format(gotthefunc,err))
                        print(msg)
                        pass
                    sleep(30)
                else:
                    try:
                        configbuilder(token=sysinfo['SYSTEM']['CONFIG']['TOKEN'])
                    except:
                        err = traceback.format_exc()
                        msg = ('Error in {} Detail: {}'.format(gotthefunc,err))
                        print(msg)
                        break
                    else:
                        try:
                            configextension(sysinfo, token=sysinfo['SYSTEM']['CONFIG']['TOKEN'])
                        except:
                            err = traceback.format_exc()
                            msg = ('Error in {} Detail: {}'.format(gotthefunc,err))
                            print(msg)
                            break
                        else:
                            subject = 'LAVA Box Self-Provisioning Succeeded'
                            message = 'LAVA Box Self-Provisioning Succeeded in extension configuration\n'
                            print(message)
                            sendemail(sysinfo,subject,message)
                            try:
                                popup('LAVA Box Self-Provisioning Succeeded','LAVA Box Self-Provisioning Succeeded',3000)
                            except:
                                err = traceback.format_exc()
                                msg = ('Error in {} Detail: {}'.format(gotthefunc,err))
                                print(msg)
                                pass
                            break
        ut, us = uptime()
        startupthreshold = 300
        startupwaittime = 15
        if ut < startupthreshold:
            msg = ('Uptime is less than {}. Pausing startup for {} seconds.'.format(startupthreshold, startupwaittime))
            sleep(startupwaittime)
        gateways = netifaces.gateways()
        try:
            sysinfo['SYSTEM']['DEFAULTGATEWAY'] = gateways['default'][netifaces.AF_INET][0]
        except:
            pass
        engines(sysinfo)       
    elif args.reconfigure:
        configbuilder()
    elif args.stats:
        stats(1)
    elif args.statsemail:
        subject = 'Stats Email'
        message = stats(2)
###### We aren't simply passing the config file email address to allow for a different recipient to receive stats. So, we're creating the variables required to run sendemail from CLI arguments and existing SMTP host data.
        statsemailinfo = {}
        statsemailinfo['SYSTEM'] = {}
        statsemailinfo['SYSTEM']['CONFIG'] = {}
        statsemailinfo['SYSTEM']['CONFIG']['EMAIL'] = args.statsemail
        statsemailinfo['SYSTEM']['CONFIG']['SMTPHOST'] = sysinfo['SYSTEM']['CONFIG']['SMTPHOST']
        sendemail(statsemailinfo,subject,message)
    elif args.maint:
        maintenance()
    elif args.mainton:
        maintenance(0,2)
    elif args.maintoff:
        maintenance(0,0)
    elif args.emailscreenshot:
        emailscreenshot(sysinfo, args.emailscreenshot)
    elif args.url:
        currenturl = getcurrenturl(sysinfo['SYSTEM']['CONFIG']['USER'])
        print(currenturl)
    elif args.shutdown:
        if os.path.exists(database):
            conn = sqlite3.connect(database)
            c = conn.cursor()
        else:
            msg = ('Database not found! Halting!')
            sendlog(gotthefunc, msg, 50)
            sys.exit()
        dbsuccess = 0
        while dbsuccess == 0:
            try:
                c.execute('UPDATE system SET strvalue = ?, notes = ? WHERE tags = ?', ('shutdown','Shutdown request from API','remediation'))
                conn.commit()
            except sqlite3.OperationalError:
                msg = ('API: Database locked. Trying again.')
                sendlog(gotthefunc, msg, 10)
                pass
                sleep(1)
            else:
                dbsuccess = 1
                conn.close()
    else:
        parser.print_help()

if __name__ == '__main__':
    sysinfo = {}
    sysinfo['SYSTEM'] = {}
    sysinfo['SYSTEM']['PID'] =  os.getpid()
    main(sysinfo)