mirror of
https://github.com/SystemRage/py-kms.git
synced 2024-11-21 15:55:37 +01:00
py-kms Gui: matched all terminal options so far done; added modes onlyserver / only client
Introduced option for asynchronous messages Slimmed down parsing process with improved errors catching
This commit is contained in:
parent
b4b57af092
commit
00793064bc
@ -592,8 +592,8 @@ def main():
|
||||
parser = Etrigan_parser()
|
||||
args = vars(parser.parse_args())
|
||||
# Check arguments.
|
||||
Etrigan_check().checkfile(args['etriganpid'], 'pidfile', '.pid')
|
||||
Etrigan_check().checkfile(args['etriganlog'], 'pidfile', '.log')
|
||||
Etrigan_check().checkfile(args['etriganpid'], '--etrigan-pid', '.pid')
|
||||
Etrigan_check().checkfile(args['etriganlog'], '--etrigan-log', '.log')
|
||||
|
||||
# Setup daemon.
|
||||
jasonblood_1 = Etrigan(pidfile = args['etriganpid'], logfile = args['etriganlog'], loglevel = args['etriganlev'],
|
||||
|
@ -140,21 +140,21 @@ class kmsBase:
|
||||
# https://docs.microsoft.com/en-us/windows/deployment/volume-activation/activate-windows-10-clients-vamt
|
||||
MinClients = kmsRequest['requiredClientCount']
|
||||
RequiredClients = MinClients * 2
|
||||
if self.srv_config["CurrentClientCount"] != None:
|
||||
if 0 < self.srv_config["CurrentClientCount"] < MinClients:
|
||||
if self.srv_config["clientcount"] != None:
|
||||
if 0 < self.srv_config["clientcount"] < MinClients:
|
||||
# fixed to 6 (product server) or 26 (product desktop)
|
||||
currentClientCount = MinClients + 1
|
||||
pretty_printer(log_obj = loggersrv.warning,
|
||||
put_text = "{reverse}{yellow}{bold}Not enough clients ! Fixed with %s, but activated client \
|
||||
could be detected as not genuine !{end}" %currentClientCount)
|
||||
elif MinClients <= self.srv_config["CurrentClientCount"] < RequiredClients:
|
||||
currentClientCount = self.srv_config["CurrentClientCount"]
|
||||
elif MinClients <= self.srv_config["clientcount"] < RequiredClients:
|
||||
currentClientCount = self.srv_config["clientcount"]
|
||||
pretty_printer(log_obj = loggersrv.warning,
|
||||
put_text = "{reverse}{yellow}{bold}With count = %s, activated client could be detected as not genuine !{end}" %currentClientCount)
|
||||
elif self.srv_config["CurrentClientCount"] >= RequiredClients:
|
||||
elif self.srv_config["clientcount"] >= RequiredClients:
|
||||
# fixed to 10 (product server) or 50 (product desktop)
|
||||
currentClientCount = RequiredClients
|
||||
if self.srv_config["CurrentClientCount"] > RequiredClients:
|
||||
if self.srv_config["clientcount"] > RequiredClients:
|
||||
pretty_printer(log_obj = loggersrv.warning,
|
||||
put_text = "{reverse}{yellow}{bold}Too many clients ! Fixed with %s{end}" %currentClientCount)
|
||||
else:
|
||||
@ -230,8 +230,8 @@ could be detected as not genuine !{end}" %currentClientCount)
|
||||
# rule: timeserver - 4h <= timeclient <= timeserver + 4h, check if is satisfied.
|
||||
response['responseTime'] = kmsRequest['requestTime']
|
||||
response['currentClientCount'] = currentClientCount
|
||||
response['vLActivationInterval'] = self.srv_config["VLActivationInterval"]
|
||||
response['vLRenewalInterval'] = self.srv_config["VLRenewalInterval"]
|
||||
response['vLActivationInterval'] = self.srv_config["activation"]
|
||||
response['vLRenewalInterval'] = self.srv_config["renewal"]
|
||||
|
||||
if self.srv_config['sqlite'] and self.srv_config['dbSupport']:
|
||||
response = sql_update_epid(self.dbName, kmsRequest, response)
|
||||
|
@ -24,6 +24,7 @@ from pykms_RpcBase import rpcBase
|
||||
from pykms_DB2Dict import kmsDB2Dict
|
||||
from pykms_Misc import check_setup
|
||||
from pykms_Misc import KmsParser, KmsParserException, KmsParserHelp
|
||||
from pykms_Misc import kms_parser_get, kms_parser_check_optionals, kms_parser_check_positionals
|
||||
from pykms_Format import justify, byterize, enco, deco, pretty_printer
|
||||
|
||||
clt_version = "py-kms_2020-02-02"
|
||||
@ -56,8 +57,10 @@ clt_options = {
|
||||
'choi' : ["WindowsVista","Windows7","Windows8","Windows8.1","Windows10","Office2010","Office2013","Office2016","Office2019"]},
|
||||
'cmid' : {'help' : 'Use this flag to manually specify a CMID to use. If no CMID is specified, a random CMID will be generated.',
|
||||
'def' : None, 'des' : "cmid"},
|
||||
'name' : {'help' : 'Use this flag to manually specify an ASCII machineName to use. If no machineName is specified a random machineName \
|
||||
will be generated.', 'def' : None, 'des' : "machineName"},
|
||||
'name' : {'help' : 'Use this flag to manually specify an ASCII machine name to use. If no machine name is specified a random one \
|
||||
will be generated.', 'def' : None, 'des' : "machine"},
|
||||
'asyncmsg' : {'help' : 'Prints pretty / logging messages asynchronously. Desactivated by default.',
|
||||
'def' : False, 'des' : "asyncmsg"},
|
||||
'llevel' : {'help' : 'Use this option to set a log level. The default is \"ERROR\".', 'def' : "ERROR", 'des' : "loglevel",
|
||||
'choi' : ["CRITICAL", "ERROR", "WARNING", "INFO", "DEBUG", "MINI"]},
|
||||
'lfile' : {'help' : 'Use this option to set an output log file. The default is \"pykms_logclient.log\". Type \"STDOUT\" to view \
|
||||
@ -67,10 +70,7 @@ log info on stdout. Type \"FILESTDOUT\" to combine previous actions.',
|
||||
}
|
||||
|
||||
def client_options():
|
||||
try:
|
||||
client_parser = KmsParser(description = clt_description, epilog = 'version: ' + clt_version, add_help = False, allow_abbrew = False)
|
||||
except TypeError:
|
||||
client_parser = KmsParser(description = clt_description, epilog = 'version: ' + clt_version, add_help = False)
|
||||
client_parser = KmsParser(description = clt_description, epilog = 'version: ' + clt_version, add_help = False)
|
||||
client_parser.add_argument("ip", nargs = "?", action = "store", default = clt_options['ip']['def'],
|
||||
help = clt_options['ip']['help'], type = str)
|
||||
client_parser.add_argument("port", nargs = "?", action = "store", default = clt_options['port']['def'],
|
||||
@ -81,6 +81,8 @@ def client_options():
|
||||
help = clt_options['cmid']['help'], type = str)
|
||||
client_parser.add_argument("-n", "--name", dest = clt_options['name']['des'] , default = clt_options['name']['def'],
|
||||
help = clt_options['name']['help'], type = str)
|
||||
client_parser.add_argument("-y", "--async-msg", action = "store_true", dest = clt_options['asyncmsg']['des'],
|
||||
default = clt_options['asyncmsg']['def'], help = clt_options['asyncmsg']['help'])
|
||||
client_parser.add_argument("-V", "--loglevel", dest = clt_options['llevel']['des'], action = "store",
|
||||
choices = clt_options['llevel']['choi'], default = clt_options['llevel']['def'],
|
||||
help = clt_options['llevel']['help'], type = str)
|
||||
@ -88,14 +90,25 @@ def client_options():
|
||||
default = clt_options['lfile']['def'], help = clt_options['lfile']['help'], type = str)
|
||||
client_parser.add_argument("-S", "--logsize", dest = clt_options['lsize']['des'], action = "store",
|
||||
default = clt_options['lsize']['def'], help = clt_options['lsize']['help'], type = float)
|
||||
|
||||
client_parser.add_argument("-h", "--help", action = "help", help = "show this help message and exit")
|
||||
|
||||
try:
|
||||
if "-h" in sys.argv[1:]:
|
||||
userarg = sys.argv[1:]
|
||||
|
||||
# Run help.
|
||||
if any(arg in ["-h", "--help"] for arg in userarg):
|
||||
KmsParserHelp().printer(parsers = [client_parser])
|
||||
clt_config.update(vars(client_parser.parse_args()))
|
||||
|
||||
# Get stored arguments.
|
||||
pykmsclt_zeroarg, pykmsclt_onearg = kms_parser_get(client_parser)
|
||||
# Update pykms options for dict client config.
|
||||
kms_parser_check_optionals(userarg, pykmsclt_zeroarg, pykmsclt_onearg, msg = 'optional py-kms client',
|
||||
exclude_opt_len = ['-F', '--logfile'])
|
||||
kms_parser_check_positionals(clt_config, client_parser.parse_args, msg = 'positional py-kms client')
|
||||
|
||||
except KmsParserException as e:
|
||||
pretty_printer(put_text = "{reverse}{red}{bold}%s. Exiting...{end}" %str(e), to_exit = True)
|
||||
pretty_printer(put_text = "{reverse}{red}{bold}%s. Exiting...{end}" %str(e), to_exit = True, where = "clt")
|
||||
|
||||
def client_check():
|
||||
# Setup and some checks.
|
||||
@ -107,18 +120,22 @@ def client_check():
|
||||
uuid.UUID(clt_config['cmid'])
|
||||
except ValueError:
|
||||
pretty_printer(log_obj = loggerclt.error, to_exit = True, where = "clt",
|
||||
put_text = "{reverse}{red}{bold}Bad CMID. Exiting...{end}")
|
||||
# Check machineName.
|
||||
if clt_config['machineName'] is not None:
|
||||
put_text = "{reverse}{red}{bold}argument `-c/--cmid`: invalid with: '%s'. Exiting...{end}" %clt_config['cmid'])
|
||||
|
||||
# Check machine name.
|
||||
if clt_config['machine'] is not None:
|
||||
try:
|
||||
clt_config['machineName'].encode('utf-16le')
|
||||
clt_config['machine'].encode('utf-16le')
|
||||
|
||||
if len(clt_config['machine']) < 2:
|
||||
pretty_printer(log_obj = loggerclt.error, to_exit = True, where = "clt",
|
||||
put_text = "{reverse}{red}{bold}argument `-n/--name`: too short (required 2 - 63 chars). Exiting...{end}")
|
||||
elif len(clt_config['machine']) > 63:
|
||||
pretty_printer(log_obj = loggerclt.error, to_exit = True, where = "clt",
|
||||
put_text = "{reverse}{red}{bold}argument `-n/--name`: too long (required 2 - 63 chars). Exiting...{end}")
|
||||
except UnicodeEncodeError:
|
||||
pretty_printer(log_obj = loggerclt.error, to_exit = True, where = "clt",
|
||||
put_text = "{reverse}{red}{bold}Bad machineName. Exiting...{end}")
|
||||
|
||||
if len(clt_config['machineName']) < 2 or len(clt_config['machineName']) > 63:
|
||||
pretty_printer(log_obj = loggerclt.error, to_exit = True, where = "clt",
|
||||
put_text = "{reverse}{red}{bold}machineName must be between 2 and 63 characters in length. Exiting...{end}")
|
||||
put_text = "{reverse}{red}{bold}argument `-n/--name`: invalid with: '%s'. Exiting...{end}" %clt_config['machine'])
|
||||
|
||||
clt_config['call_id'] = 1
|
||||
|
||||
@ -149,8 +166,14 @@ def client_update():
|
||||
|
||||
def client_create():
|
||||
loggerclt.info("Connecting to %s on port %d..." % (clt_config['ip'], clt_config['port']))
|
||||
s = socket.create_connection((clt_config['ip'], clt_config['port']))
|
||||
loggerclt.info("Connection successful !")
|
||||
try:
|
||||
s = socket.create_connection((clt_config['ip'], clt_config['port']))
|
||||
loggerclt.info("Connection successful !")
|
||||
except (socket.gaierror, socket.error) as e:
|
||||
pretty_printer(log_obj = loggerclt.error, to_exit = True, where = "clt",
|
||||
put_text = "{reverse}{red}{bold}Connection failed '%s:%d': %s. Exiting...{end}" %(clt_config['ip'],
|
||||
clt_config['port'],
|
||||
str(e)))
|
||||
binder = pykms_RpcBind.handler(None, clt_config)
|
||||
RPC_Bind = enco(str(binder.generateRequest()), 'latin-1')
|
||||
|
||||
@ -248,7 +271,7 @@ def createKmsRequestBase():
|
||||
requestDict['previousClientMachineId'] = '\0' * 16 # I'm pretty sure this is supposed to be a null UUID.
|
||||
requestDict['requiredClientCount'] = clt_config['RequiredClientCount']
|
||||
requestDict['requestTime'] = dt_to_filetime(datetime.datetime.utcnow())
|
||||
requestDict['machineName'] = (clt_config['machineName'] if (clt_config['machineName'] is not None) else
|
||||
requestDict['machineName'] = (clt_config['machine'] if (clt_config['machine'] is not None) else
|
||||
''.join(random.choice(string.ascii_letters + string.digits) for i in range(random.randint(2,63)))).encode('utf-16le')
|
||||
requestDict['mnPad'] = '\0'.encode('utf-16le') * (63 - len(requestDict['machineName'].decode('utf-16le')))
|
||||
|
||||
|
@ -5,6 +5,7 @@ import re
|
||||
import sys
|
||||
import os
|
||||
from collections import OrderedDict
|
||||
import logging
|
||||
|
||||
try:
|
||||
# Python 2.x imports
|
||||
@ -16,6 +17,7 @@ except ImportError:
|
||||
import queue as Queue
|
||||
|
||||
pyver = sys.version_info[:2]
|
||||
|
||||
#----------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
def enco(strg, typ = 'latin-1'):
|
||||
@ -195,13 +197,15 @@ if pyver < (3, 3):
|
||||
file = kwargs.get('file', sys.stdout)
|
||||
file.flush() if file is not None else sys.stdout.flush()
|
||||
|
||||
# based on: https://ryanjoneil.github.io/posts/2014-02-14-capturing-stdout-in-a-python-child-process.html,
|
||||
# but not using threading/multiprocessing so:
|
||||
# 1) message visualization order preserved.
|
||||
# 2) newlines_count function output not wrong.
|
||||
# based on: https://ryanjoneil.github.io/posts/2014-02-14-capturing-stdout-in-a-python-child-process.html
|
||||
queue_print = Queue.Queue()
|
||||
|
||||
class ShellMessage(object):
|
||||
view = True
|
||||
count, remain, numlist = (0, 0, [])
|
||||
viewsrv, viewclt = (True for _ in range(2))
|
||||
asyncmsgsrv, asyncmsgclt = (False for _ in range(2))
|
||||
indx, count, remain, numlist = (0, 0, 0, [])
|
||||
loggersrv_pty = logging.getLogger('logsrvpty')
|
||||
loggerclt_pty = logging.getLogger('logcltpty')
|
||||
|
||||
class Collect(StringIO):
|
||||
# Capture string sent to stdout.
|
||||
@ -215,8 +219,9 @@ class ShellMessage(object):
|
||||
self.put_text = put_text
|
||||
self.where = where
|
||||
self.plaintext = []
|
||||
self.path = os.path.dirname(os.path.abspath( __file__ )) + '/pykms_newlines.txt'
|
||||
self.print_queue = Queue.Queue()
|
||||
self.path_nl = os.path.dirname(os.path.abspath( __file__ )) + '/pykms_newlines.txt'
|
||||
self.path_clean_nl = os.path.dirname(os.path.abspath( __file__ )) + '/pykms_clean_newlines.txt'
|
||||
self.queue_get = Queue.Queue()
|
||||
|
||||
def formatter(self, msgtofrmt):
|
||||
if self.newlines:
|
||||
@ -236,14 +241,14 @@ class ShellMessage(object):
|
||||
|
||||
def newlines_file(self, mode, *args):
|
||||
try:
|
||||
with open(self.path, mode) as file:
|
||||
with open(self.path_nl, mode) as file:
|
||||
if mode in ['w', 'a']:
|
||||
file.write(args[0])
|
||||
elif mode == 'r':
|
||||
data = [int(i) for i in [line.rstrip('\n') for line in file.readlines()]]
|
||||
self.newlines, ShellMessage.remain = data[0], sum(data[1:])
|
||||
except:
|
||||
with open(self.path, 'w') as file:
|
||||
with open(self.path_nl, 'w') as file:
|
||||
pass
|
||||
|
||||
def newlines_count(self, num):
|
||||
@ -265,40 +270,114 @@ class ShellMessage(object):
|
||||
self.continuecount = True
|
||||
elif num in [-2 ,-4]:
|
||||
self.newlines_file('r')
|
||||
if num == 21:
|
||||
ShellMessage.count, ShellMessage.remain, ShellMessage.numlist = (0, 0, [])
|
||||
os.remove(self.path)
|
||||
|
||||
def run(self):
|
||||
# view = False part.
|
||||
if not ShellMessage.view:
|
||||
if self.get_text:
|
||||
self.newlines = 0
|
||||
if self.put_text is not None:
|
||||
for msg in self.put_text:
|
||||
self.formatter(msg)
|
||||
else:
|
||||
for num in self.nshell:
|
||||
self.formatter(MsgMap[num])
|
||||
return self.plaintext
|
||||
self.newlines_clean(num)
|
||||
|
||||
def newlines_clean(self, num):
|
||||
if num == 0:
|
||||
with open(self.path_clean_nl, 'w') as file:
|
||||
file.write('clean newlines')
|
||||
try:
|
||||
with open(self.path_clean_nl, 'r') as file:
|
||||
some = file.read()
|
||||
if num == 21:
|
||||
ShellMessage.count, ShellMessage.remain, ShellMessage.numlist = (0, 0, [])
|
||||
os.remove(self.path_nl)
|
||||
os.remove(self.path_clean_nl)
|
||||
except:
|
||||
if num == 19:
|
||||
ShellMessage.count, ShellMessage.remain, ShellMessage.numlist = (0, 0, [])
|
||||
os.remove(self.path_nl)
|
||||
|
||||
def putter(self, aqueue, toput):
|
||||
try:
|
||||
aqueue.put_nowait(toput)
|
||||
except Queue.Full:
|
||||
pass
|
||||
|
||||
def execute(self):
|
||||
self.manage()
|
||||
ShellMessage.indx += 1
|
||||
|
||||
def print_logging_setup(self, logger, async_flag, formatter = logging.Formatter('%(name)s %(message)s')):
|
||||
from pykms_GuiBase import gui_redirector
|
||||
stream = gui_redirector(StringIO())
|
||||
handler = logging.StreamHandler(stream)
|
||||
handler.name = 'LogStream'
|
||||
handler.setLevel(logging.INFO)
|
||||
handler.setFormatter(formatter)
|
||||
|
||||
if logger.handlers:
|
||||
logger.handlers = []
|
||||
|
||||
if async_flag:
|
||||
from pykms_Misc import MultiProcessingLogHandler
|
||||
logger.addHandler(MultiProcessingLogHandler('Thread-AsyncMsg{0}'.format(handler.name), handler = handler))
|
||||
else:
|
||||
logger.addHandler(handler)
|
||||
logger.setLevel(logging.INFO)
|
||||
|
||||
def print_logging(self, toprint):
|
||||
if (self.nshell and ((0 in self.nshell) or (2 in self.nshell and not ShellMessage.viewclt))) or ShellMessage.indx == 0:
|
||||
from pykms_GuiBase import gui_redirector_setup, gui_redirector_clear
|
||||
gui_redirector_setup()
|
||||
gui_redirector_clear()
|
||||
self.print_logging_setup(ShellMessage.loggersrv_pty, ShellMessage.asyncmsgsrv)
|
||||
self.print_logging_setup(ShellMessage.loggerclt_pty, ShellMessage.asyncmsgclt)
|
||||
|
||||
if self.where == 'srv':
|
||||
ShellMessage.loggersrv_pty.info(toprint)
|
||||
elif self.where == 'clt':
|
||||
ShellMessage.loggerclt_pty.info(toprint)
|
||||
|
||||
def notview(self):
|
||||
if self.get_text:
|
||||
self.newlines = 0
|
||||
if self.put_text is not None:
|
||||
for msg in self.put_text:
|
||||
self.formatter(msg)
|
||||
else:
|
||||
for num in self.nshell:
|
||||
self.formatter(MsgMap[num])
|
||||
self.putter(self.queue_get, self.plaintext)
|
||||
|
||||
def manage(self):
|
||||
if not ShellMessage.viewsrv:
|
||||
# viewsrv = False, viewclt = True.
|
||||
if ShellMessage.viewclt:
|
||||
if self.where == 'srv':
|
||||
self.notview()
|
||||
return
|
||||
else:
|
||||
# viewsrv = False, viewclt = False.
|
||||
self.notview()
|
||||
return
|
||||
else:
|
||||
# viewsrv = True, viewclt = False.
|
||||
if not ShellMessage.viewclt:
|
||||
if self.where == 'clt':
|
||||
self.notview()
|
||||
return
|
||||
else:
|
||||
# viewsrv = True, viewclt = True.
|
||||
pass
|
||||
|
||||
# Do job.
|
||||
self.produce()
|
||||
toprint = self.consume(timeout = 0.1)
|
||||
# Redirect output.
|
||||
toprint = self.consume(queue_print, timeout = 0.1)
|
||||
|
||||
if sys.stdout.isatty():
|
||||
print(toprint)
|
||||
print(toprint, flush = True)
|
||||
else:
|
||||
try:
|
||||
# Import after variables creation.
|
||||
from pykms_GuiBase import gui_redirect
|
||||
gui_redirect(toprint, self.where)
|
||||
self.print_logging(toprint)
|
||||
except:
|
||||
print(toprint)
|
||||
print(toprint, flush = True)
|
||||
|
||||
# Get string/s printed.
|
||||
if self.get_text:
|
||||
return self.plaintext
|
||||
self.putter(self.queue_get, self.plaintext)
|
||||
return
|
||||
|
||||
def produce(self):
|
||||
# Save everything that would otherwise go to stdout.
|
||||
@ -327,15 +406,12 @@ class ShellMessage(object):
|
||||
finally:
|
||||
# Restore stdout and send content.
|
||||
sys.stdout = sys.__stdout__
|
||||
try:
|
||||
self.print_queue.put(outstream.getvalue())
|
||||
except Queue.Full:
|
||||
pass
|
||||
self.putter(queue_print, outstream.getvalue())
|
||||
|
||||
def consume(self, timeout = None):
|
||||
def consume(self, aqueue, timeout = None):
|
||||
try:
|
||||
toprint = self.print_queue.get(block = timeout is not None, timeout = timeout)
|
||||
self.print_queue.task_done()
|
||||
toprint = aqueue.get(block = timeout is not None, timeout = timeout)
|
||||
aqueue.task_done()
|
||||
return toprint
|
||||
except Queue.Empty:
|
||||
return None
|
||||
@ -381,11 +457,13 @@ def pretty_printer(**kwargs):
|
||||
options['get_text'] = False
|
||||
|
||||
# Process messages.
|
||||
plain_messages = ShellMessage.Process(options['num_text'],
|
||||
get_text = options['get_text'],
|
||||
put_text = options['put_text'],
|
||||
where = options['where']).run()
|
||||
shmsg = ShellMessage.Process(options['num_text'],
|
||||
get_text = options['get_text'],
|
||||
put_text = options['put_text'],
|
||||
where = options['where'])
|
||||
|
||||
shmsg.execute()
|
||||
plain_messages = shmsg.consume(shmsg.queue_get, timeout = None)
|
||||
if options['log_obj']:
|
||||
for plain_message in plain_messages:
|
||||
options['log_obj'](plain_message)
|
||||
|
@ -48,25 +48,36 @@ def get_ip_address():
|
||||
else:
|
||||
ip = 'Unknown'
|
||||
return ip
|
||||
|
||||
def gui_redirect(str_to_print, where):
|
||||
global txsrv, txclt, txcol
|
||||
|
||||
def gui_redirector(stream, redirect_to = TextRedirect.Pretty, redirect_conditio = True, stderr_side = "srv"):
|
||||
global txsrv, txclt, txcol
|
||||
if redirect_conditio:
|
||||
if stream == 'stdout':
|
||||
sys.stdout = redirect_to(txsrv, txclt, txcol)
|
||||
elif stream == 'stderr':
|
||||
sys.stderr = redirect_to(txsrv, txclt, txcol, stderr_side)
|
||||
else:
|
||||
stream = redirect_to(txsrv, txclt, txcol)
|
||||
return stream
|
||||
|
||||
def gui_redirector_setup():
|
||||
TextRedirect.Pretty.tag_num = 0
|
||||
TextRedirect.Pretty.newlinecut = [-1, -2, -4, -5]
|
||||
|
||||
def gui_redirector_clear():
|
||||
global txsrv, oysrv
|
||||
try:
|
||||
TextRedirect.StdoutRedirect(txsrv, txclt, txcol, str_to_print, where)
|
||||
if oysrv:
|
||||
txsrv.configure(state = 'normal')
|
||||
txsrv.delete('1.0', 'end')
|
||||
txsrv.configure(state = 'disabled')
|
||||
except:
|
||||
print(str_to_print)
|
||||
# self.onlysrv not defined (menu not used)
|
||||
pass
|
||||
|
||||
##-----------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
class KmsGui(tk.Tk):
|
||||
def browse(self, entrywidget, options):
|
||||
path = filedialog.askdirectory()
|
||||
if os.path.isdir(path):
|
||||
entrywidget.delete('0', 'end')
|
||||
entrywidget.insert('end', path + os.sep + os.path.basename(options['lfile']['def']))
|
||||
|
||||
|
||||
class KmsGui(tk.Tk):
|
||||
def __init__(self, *args, **kwargs):
|
||||
tk.Tk.__init__(self, *args, **kwargs)
|
||||
self.wraplength = 200
|
||||
@ -96,19 +107,76 @@ class KmsGui(tk.Tk):
|
||||
self.option_add('*TCombobox*Listbox.font', self.optfontredux)
|
||||
|
||||
self.gui_create()
|
||||
|
||||
def browse(self, entrywidget, options):
|
||||
path = filedialog.askdirectory()
|
||||
if os.path.isdir(path):
|
||||
entrywidget.delete('0', 'end')
|
||||
entrywidget.insert('end', path + os.sep + os.path.basename(options['lfile']['def']))
|
||||
|
||||
def invert(self, widgets = []):
|
||||
for widget in widgets:
|
||||
if widget['state'] == 'normal':
|
||||
widget.configure(state = 'disabled')
|
||||
elif widget['state'] == 'disabled':
|
||||
widget.configure(state = 'normal')
|
||||
|
||||
def gui_menu(self):
|
||||
self.onlysrv, self.onlyclt = (False for _ in range(2))
|
||||
menubar = tk.Menu(self)
|
||||
prefmenu = tk.Menu(menubar, tearoff = 0, font = ("Noto Sans Regular", 10), borderwidth = 3, relief = 'ridge')
|
||||
menubar.add_cascade(label = 'Preferences', menu = prefmenu)
|
||||
prefmenu.add_command(label = 'Enable server-side mode', command = lambda: self.pref_onlysrv(prefmenu))
|
||||
prefmenu.add_command(label = 'Enable client-side mode', command = lambda: self.pref_onlyclt(prefmenu))
|
||||
self.config(menu = menubar)
|
||||
|
||||
def pref_onlysrv(self, menu):
|
||||
global oysrv
|
||||
|
||||
if self.onlyclt or serverthread.is_running_server:
|
||||
return
|
||||
self.onlysrv = not self.onlysrv
|
||||
if self.onlysrv:
|
||||
menu.entryconfigure(0, label = 'Disable server-side mode')
|
||||
self.clt_on_show(force_remove = True)
|
||||
else:
|
||||
menu.entryconfigure(0, label = 'Enable server-side mode')
|
||||
self.invert(widgets = [self.shbtnclt])
|
||||
oysrv = self.onlysrv
|
||||
|
||||
def pref_onlyclt(self, menu):
|
||||
if self.onlysrv or serverthread.is_running_server:
|
||||
return
|
||||
self.onlyclt = not self.onlyclt
|
||||
if self.onlyclt:
|
||||
menu.entryconfigure(1, label = 'Disable client-side mode')
|
||||
if self.shbtnclt['text'] == 'SHOW\nCLIENT':
|
||||
self.clt_on_show(force_view = True)
|
||||
self.optsrvwin.grid_remove()
|
||||
self.msgsrvwin.grid_remove()
|
||||
gui_redirector('stderr', redirect_to = TextRedirect.Stderr, stderr_side = "clt")
|
||||
else:
|
||||
menu.entryconfigure(1, label = 'Enable client-side mode')
|
||||
self.optsrvwin.grid()
|
||||
self.msgsrvwin.grid()
|
||||
gui_redirector('stderr', redirect_to = TextRedirect.Stderr)
|
||||
|
||||
self.invert(widgets = [self.runbtnsrv, self.shbtnclt, self.runbtnclt])
|
||||
|
||||
def gui_create(self):
|
||||
## Create server gui
|
||||
self.gui_srv()
|
||||
## Create client gui + other operations.
|
||||
self.gui_complete()
|
||||
## Create menu.
|
||||
self.gui_menu()
|
||||
## Create globals for printing process (redirect stdout).
|
||||
global txsrv, txclt, txcol
|
||||
txsrv = self.textboxsrv.get()
|
||||
txclt = self.textboxclt.get()
|
||||
txcol = self.customcolors
|
||||
## Redirect stderr.
|
||||
sys.stderr = TextRedirect.StderrRedirect(txsrv, txclt, txcol)
|
||||
gui_redirector('stderr', redirect_to = TextRedirect.Stderr)
|
||||
|
||||
def gui_pages_show(self, pagename, side):
|
||||
# https://stackoverflow.com/questions/7546050/switch-between-two-frames-in-tkinter
|
||||
@ -162,7 +230,7 @@ class KmsGui(tk.Tk):
|
||||
btnani = tk.Button(aniwin)
|
||||
btnani.grid(row = 0, column = col[2], padx = 2, pady = 2, sticky = stick)
|
||||
self.pagewidgets[side]["BtnAni"][position] = btnani
|
||||
# customize buttons.
|
||||
## Customize buttons.
|
||||
custom_pages(self, side)
|
||||
|
||||
def gui_pages_create(self, parent, side, create = {}):
|
||||
@ -199,7 +267,7 @@ class KmsGui(tk.Tk):
|
||||
self.btnsrvwin = tk.Canvas(self.masterwin, background = self.customcolors['white'], borderwidth = 3, relief = 'ridge')
|
||||
self.optsrvwin = tk.Canvas(self.masterwin, background = self.customcolors['white'], borderwidth = 3, relief = 'ridge')
|
||||
self.msgsrvwin = tk.Frame(self.masterwin, background = self.customcolors['black'], relief = 'ridge', width = 300, height = 200)
|
||||
|
||||
|
||||
## Layout main containers.
|
||||
self.masterwin.grid(row = 0, column = 0, sticky = 'nsew')
|
||||
self.btnsrvwin.grid(row = 0, column = 1, padx = 2, pady = 2, sticky = 'nw')
|
||||
@ -209,11 +277,11 @@ class KmsGui(tk.Tk):
|
||||
|
||||
self.pagewidgets = {}
|
||||
|
||||
## subpages of optsrvwin.
|
||||
## Subpages of "optsrvwin".
|
||||
self.gui_pages_create(parent = self.optsrvwin, side = "Srv", create = {"PageStart": None,
|
||||
"PageEnd": None})
|
||||
|
||||
## continue to grid.
|
||||
## Continue to grid.
|
||||
self.msgsrvwin.grid(row = 1, column = 2, padx = 1, pady = 1, sticky = 'nsew')
|
||||
self.msgsrvwin.grid_propagate(False)
|
||||
self.msgsrvwin.grid_columnconfigure(0, weight = 1)
|
||||
@ -285,7 +353,7 @@ class KmsGui(tk.Tk):
|
||||
self.activ.insert('end', str(srv_options['activation']['def']))
|
||||
ToolTip(self.activ, text = srv_options['activation']['help'], wraplength = self.wraplength)
|
||||
# Renewal Interval.
|
||||
renewlbl = tk.Label(self.pagewidgets["Srv"]["PageWin"]["PageStart"], text = 'Activation Interval: ', font = self.optfont)
|
||||
renewlbl = tk.Label(self.pagewidgets["Srv"]["PageWin"]["PageStart"], text = 'Renewal Interval: ', font = self.optfont)
|
||||
self.renew = tk.Entry(self.pagewidgets["Srv"]["PageWin"]["PageStart"], width = 10, font = self.optfont, validate = "key",
|
||||
validatecommand = self.validation_int)
|
||||
self.renew.insert('end', str(srv_options['renewal']['def']))
|
||||
@ -298,28 +366,34 @@ class KmsGui(tk.Tk):
|
||||
ToolTip(self.srvfile, text = srv_options['lfile']['help'], wraplength = self.wraplength)
|
||||
srvfilebtnwin = tk.Button(self.pagewidgets["Srv"]["PageWin"]["PageStart"], text = 'Browse',
|
||||
command = lambda: self.browse(self.srvfile, srv_options))
|
||||
|
||||
# Loglevel.
|
||||
srvlevellbl = tk.Label(self.pagewidgets["Srv"]["PageWin"]["PageStart"], text = 'Loglevel: ', font = self.optfont)
|
||||
self.srvlevel = ttk.Combobox(self.pagewidgets["Srv"]["PageWin"]["PageStart"], values = tuple(srv_options['llevel']['choi']),
|
||||
width = 10, height = 10, font = self.optfontredux, state = "readonly")
|
||||
self.srvlevel.set(srv_options['llevel']['def'])
|
||||
ToolTip(self.srvlevel, text = srv_options['llevel']['help'], wraplength = self.wraplength)
|
||||
|
||||
self.chksrvfile = ListboxOfRadiobuttons(self.pagewidgets["Srv"]["PageWin"]["PageStart"],
|
||||
['FILE', 'FILEOFF', 'STDOUT', 'STDOUTOFF', 'FILESTDOUT'],
|
||||
self.optfontredux,
|
||||
changed = [(self.srvfile, srv_options['lfile']['def']),
|
||||
(srvfilebtnwin, ''),
|
||||
(self.srvlevel, srv_options['llevel']['def'])],
|
||||
width = 10, height = 1, borderwidth = 2, relief = 'ridge')
|
||||
|
||||
# Logsize.
|
||||
srvsizelbl = tk.Label(self.pagewidgets["Srv"]["PageWin"]["PageStart"], text = 'Logsize: ', font = self.optfont)
|
||||
self.srvsize = tk.Entry(self.pagewidgets["Srv"]["PageWin"]["PageStart"], width = 10, font = self.optfont, validate = "key",
|
||||
validatecommand = self.validation_float)
|
||||
self.srvsize.insert('end', srv_options['lsize']['def'])
|
||||
ToolTip(self.srvsize, text = srv_options['lsize']['help'], wraplength = self.wraplength)
|
||||
# Asynchronous messages.
|
||||
self.chkvalsrvasy = tk.BooleanVar()
|
||||
self.chkvalsrvasy.set(srv_options['asyncmsg']['def'])
|
||||
chksrvasy = tk.Checkbutton(self.pagewidgets["Srv"]["PageWin"]["PageStart"], text = 'Async\nMsg',
|
||||
font = self.optfontredux, var = self.chkvalsrvasy, relief = 'groove')
|
||||
ToolTip(chksrvasy, text = srv_options['asyncmsg']['help'], wraplength = self.wraplength)
|
||||
|
||||
# Listbox radiobuttons server.
|
||||
self.chksrvfile = ListboxOfRadiobuttons(self.pagewidgets["Srv"]["PageWin"]["PageStart"],
|
||||
['FILE', 'FILEOFF', 'STDOUT', 'STDOUTOFF', 'FILESTDOUT'],
|
||||
self.optfontredux,
|
||||
changed = [(self.srvfile, srv_options['lfile']['def']),
|
||||
(srvfilebtnwin, ''),
|
||||
(self.srvsize, srv_options['lsize']['def']),
|
||||
(self.srvlevel, srv_options['llevel']['def'])],
|
||||
width = 10, height = 1, borderwidth = 2, relief = 'ridge')
|
||||
|
||||
## Layout widgets (optsrvwin:Srv:PageWin:PageStart)
|
||||
ver.grid(row = 0, column = 0, columnspan = 3, padx = 5, pady = 5, sticky = 'ew')
|
||||
@ -344,6 +418,7 @@ class KmsGui(tk.Tk):
|
||||
self.srvfile.grid(row = 10, column = 1, padx = 5, pady = 5, sticky = 'ew')
|
||||
srvfilebtnwin.grid(row = 10, column = 2, padx = 5, pady = 5, sticky = 'ew')
|
||||
self.chksrvfile.grid(row = 11, column = 1, padx = 5, pady = 5, sticky = 'ew')
|
||||
chksrvasy.grid(row = 11, column = 2, padx = 5, pady = 5, sticky = 'ew')
|
||||
srvlevellbl.grid(row = 12, column = 0, padx = 5, pady = 5, sticky = 'e')
|
||||
self.srvlevel.grid(row = 12, column = 1, padx = 5, pady = 5, sticky = 'ew')
|
||||
srvsizelbl.grid(row = 13, column = 0, padx = 5, pady = 5, sticky = 'e')
|
||||
@ -352,23 +427,25 @@ class KmsGui(tk.Tk):
|
||||
## Create widgets (optsrvwin:Srv:PageWin:PageEnd)-------------------------------------------------------------------------------------------
|
||||
# Timeout connection.
|
||||
timeout0lbl = tk.Label(self.pagewidgets["Srv"]["PageWin"]["PageEnd"], text = 'Timeout connection: ', font = self.optfont)
|
||||
self.timeout0 = tk.Entry(self.pagewidgets["Srv"]["PageWin"]["PageEnd"], width = 10, font = self.optfont)
|
||||
self.timeout0 = tk.Entry(self.pagewidgets["Srv"]["PageWin"]["PageEnd"], width = 16, font = self.optfont)
|
||||
self.timeout0.insert('end', str(srv_options['time0']['def']))
|
||||
ToolTip(self.timeout0, text = srv_options['time0']['help'], wraplength = self.wraplength)
|
||||
# Sqlite database.
|
||||
self.chkvalsql = tk.BooleanVar()
|
||||
self.chkvalsql.set(srv_options['sql']['def'])
|
||||
chksql = tk.Checkbutton(self.pagewidgets["Srv"]["PageWin"]["PageEnd"], text = 'Create Sqlite\nDatabase',
|
||||
font = self.optfont, var = self.chkvalsql)
|
||||
font = self.optfontredux, var = self.chkvalsql, relief = 'groove')
|
||||
ToolTip(chksql, text = srv_options['sql']['help'], wraplength = self.wraplength)
|
||||
|
||||
## Layout widgets (optsrvwin:Srv:PageWin:PageEnd)
|
||||
tk.Label(self.pagewidgets["Srv"]["PageWin"]["PageEnd"], width = 0, height = 0).grid(row = 0, column = 0, padx = 5, pady = 5, sticky = 'nw')
|
||||
# a label for vertical aligning with PageStart
|
||||
tk.Label(self.pagewidgets["Srv"]["PageWin"]["PageEnd"], width = 0,
|
||||
height = 0, bg = self.customcolors['lavender']).grid(row = 0, column = 0, padx = 5, pady = 5, sticky = 'nw')
|
||||
timeout0lbl.grid(row = 1, column = 0, padx = 5, pady = 5, sticky = 'e')
|
||||
self.timeout0.grid(row = 1, column = 1, padx = 5, pady = 5, sticky = 'ew')
|
||||
chksql.grid(row = 2, column = 1, padx = 5, pady = 5, sticky = 'ew')
|
||||
self.timeout0.grid(row = 1, column = 1, padx = 5, pady = 5, sticky = 'w')
|
||||
chksql.grid(row = 2, column = 1, padx = 5, pady = 5, sticky = 'w')
|
||||
|
||||
# Store Srv widgets.
|
||||
# Store server-side widgets.
|
||||
self.storewidgets_srv = self.gui_store(side = "Srv", typewidgets = ['Button', 'Entry', 'TCombobox', 'Checkbutton'])
|
||||
self.storewidgets_srv.append(self.chksrvfile)
|
||||
|
||||
@ -377,28 +454,52 @@ class KmsGui(tk.Tk):
|
||||
relief = 'ridge', font = self.msgfont)
|
||||
self.textboxsrv.put()
|
||||
|
||||
def always_centered(self, geo, centered, refs):
|
||||
x = (self.winfo_screenwidth() // 2) - (self.winfo_width() // 2)
|
||||
y = (self.winfo_screenheight() // 2) - (self.winfo_height() // 2)
|
||||
w, h, dx, dy = geo.split('+')[0].split('x') + geo.split('+')[1:]
|
||||
|
||||
if w == refs[1]:
|
||||
if centered:
|
||||
self.geometry('+%d+%d' %(x, y))
|
||||
centered = False
|
||||
elif w == refs[0]:
|
||||
if not centered:
|
||||
self.geometry('+%d+%d' %(x, y))
|
||||
centered = True
|
||||
|
||||
if dx != str(x) or dy != str(y):
|
||||
self.geometry('+%d+%d' %(x, 0))
|
||||
|
||||
self.after(200, self.always_centered, self.geometry(), centered, refs)
|
||||
|
||||
def gui_complete(self):
|
||||
## Create client widgets (optcltwin, msgcltwin, btncltwin)
|
||||
self.update_idletasks() # update Gui to get btnsrvwin values --> btncltwin.
|
||||
minw, minh = self.winfo_width(), self.winfo_height()
|
||||
self.iconify()
|
||||
self.gui_clt()
|
||||
minw, minh = self.winfo_width(), self.winfo_height()
|
||||
# Main window custom background.
|
||||
maxw, minh = self.winfo_width(), self.winfo_height()
|
||||
## Main window custom background.
|
||||
self.update_idletasks() # update Gui for custom background
|
||||
self.iconify()
|
||||
custom_background(self)
|
||||
# Main window other modifications.
|
||||
## Main window other modifications.
|
||||
self.eval('tk::PlaceWindow %s center' %self.winfo_pathname(self.winfo_id()))
|
||||
self.wm_attributes("-topmost", True)
|
||||
self.protocol("WM_DELETE_WINDOW", lambda:0)
|
||||
self.minsize(minw, minh)
|
||||
self.resizable(True, False)
|
||||
|
||||
def get_position(self, genericwidget):
|
||||
x, y = (genericwidget.winfo_x(), genericwidget.winfo_y())
|
||||
w, h = (genericwidget.winfo_width(), genericwidget.winfo_height())
|
||||
self.protocol("WM_DELETE_WINDOW", lambda: 0)
|
||||
## Disable maximize button.
|
||||
self.resizable(False, False)
|
||||
## Centered window.
|
||||
self.always_centered(self.geometry(), False, [minw, maxw])
|
||||
|
||||
def get_position(self, widget):
|
||||
x, y = (widget.winfo_x(), widget.winfo_y())
|
||||
w, h = (widget.winfo_width(), widget.winfo_height())
|
||||
return x, y, w, h
|
||||
|
||||
def gui_clt(self):
|
||||
def gui_clt(self):
|
||||
self.count_clear = 0
|
||||
self.optcltwin = tk.Canvas(self.masterwin, background = self.customcolors['white'], borderwidth = 3, relief = 'ridge')
|
||||
self.msgcltwin = tk.Frame(self.masterwin, background = self.customcolors['black'], relief = 'ridge', width = 300, height = 200)
|
||||
self.btncltwin = tk.Canvas(self.masterwin, background = self.customcolors['white'], borderwidth = 3, relief = 'ridge')
|
||||
@ -411,29 +512,25 @@ class KmsGui(tk.Tk):
|
||||
self.optcltwin.grid_rowconfigure(0, weight = 1)
|
||||
self.optcltwin.grid_columnconfigure(1, weight = 1)
|
||||
|
||||
# subpages of optcltwin.
|
||||
## Subpages of "optcltwin".
|
||||
self.gui_pages_create(parent = self.optcltwin, side = "Clt", create = {"PageStart": None,
|
||||
"PageEnd": None})
|
||||
|
||||
# continue to grid.
|
||||
## Continue to grid.
|
||||
self.msgcltwin.grid(row = 1, column = 4, padx = 1, pady = 1, sticky = 'nsew')
|
||||
self.msgcltwin.grid_propagate(False)
|
||||
self.msgcltwin.grid_columnconfigure(0, weight = 1)
|
||||
self.msgcltwin.grid_rowconfigure(0, weight = 1)
|
||||
|
||||
# Create widgets (btncltwin) ----------------------------------------------------------------------------------------------------------------
|
||||
## Create widgets (btncltwin) ----------------------------------------------------------------------------------------------------------------
|
||||
self.runbtnclt = tk.Button(self.btncltwin, text = 'START\nCLIENT', background = self.customcolors['blue'],
|
||||
foreground = self.customcolors['white'], relief = 'flat', font = self.btnwinfont,
|
||||
state = 'disabled', command = self.clt_on_start)
|
||||
|
||||
## self.othbutt = tk.Button(self.btncltwin, text = 'Botton\n2', background = self.customcolors['green'],
|
||||
## foreground = self.customcolors['white'], relief = 'flat', font = self.btnwinfont)
|
||||
|
||||
# Layout widgets (btncltwin)
|
||||
|
||||
## Layout widgets (btncltwin)
|
||||
self.runbtnclt.grid(row = 0, column = 0, padx = 2, pady = 2, sticky = 'ew')
|
||||
## self.othbutt.grid(row = 1, column = 0, padx = 2, pady = 2, sticky = 'ew')
|
||||
|
||||
# Create widgets (optcltwin:Clt:PageWin:PageStart) ------------------------------------------------------------------------------------------
|
||||
## Create widgets (optcltwin:Clt:PageWin:PageStart) ------------------------------------------------------------------------------------------
|
||||
# Version.
|
||||
cltver = tk.Label(self.pagewidgets["Clt"]["PageWin"]["PageStart"], text = 'You are running client version: ' + clt_version,
|
||||
foreground = self.customcolors['red'], font = self.othfont)
|
||||
@ -479,21 +576,30 @@ class KmsGui(tk.Tk):
|
||||
self.cltlevel.set(clt_options['llevel']['def'])
|
||||
ToolTip(self.cltlevel, text = clt_options['llevel']['help'], wraplength = self.wraplength)
|
||||
|
||||
self.chkcltfile = ListboxOfRadiobuttons(self.pagewidgets["Clt"]["PageWin"]["PageStart"],
|
||||
['FILE', 'FILEOFF', 'STDOUT', 'STDOUTOFF', 'FILESTDOUT'],
|
||||
self.optfontredux,
|
||||
changed = [(self.cltfile, clt_options['lfile']['def']),
|
||||
(cltfilebtnwin, ''),
|
||||
(self.cltlevel, clt_options['llevel']['def'])],
|
||||
width = 10, height = 1, borderwidth = 2, relief = 'ridge')
|
||||
# Logsize.
|
||||
cltsizelbl = tk.Label(self.pagewidgets["Clt"]["PageWin"]["PageStart"], text = 'Logsize: ', font = self.optfont)
|
||||
self.cltsize = tk.Entry(self.pagewidgets["Clt"]["PageWin"]["PageStart"], width = 10, font = self.optfont, validate = "key",
|
||||
validatecommand = self.validation_float)
|
||||
self.cltsize.insert('end', clt_options['lsize']['def'])
|
||||
ToolTip(self.cltsize, text = clt_options['lsize']['help'], wraplength = self.wraplength)
|
||||
# Asynchronous messages.
|
||||
self.chkvalcltasy = tk.BooleanVar()
|
||||
self.chkvalcltasy.set(clt_options['asyncmsg']['def'])
|
||||
chkcltasy = tk.Checkbutton(self.pagewidgets["Clt"]["PageWin"]["PageStart"], text = 'Async\nMsg',
|
||||
font = self.optfontredux, var = self.chkvalcltasy, relief = 'groove')
|
||||
ToolTip(chkcltasy, text = clt_options['asyncmsg']['help'], wraplength = self.wraplength)
|
||||
|
||||
# Listbox radiobuttons client.
|
||||
self.chkcltfile = ListboxOfRadiobuttons(self.pagewidgets["Clt"]["PageWin"]["PageStart"],
|
||||
['FILE', 'FILEOFF', 'STDOUT', 'STDOUTOFF', 'FILESTDOUT'],
|
||||
self.optfontredux,
|
||||
changed = [(self.cltfile, clt_options['lfile']['def']),
|
||||
(cltfilebtnwin, ''),
|
||||
(self.cltsize, clt_options['lsize']['def']),
|
||||
(self.cltlevel, clt_options['llevel']['def'])],
|
||||
width = 10, height = 1, borderwidth = 2, relief = 'ridge')
|
||||
|
||||
# Layout widgets (optcltwin:Clt:PageWin:PageStart)
|
||||
## Layout widgets (optcltwin:Clt:PageWin:PageStart)
|
||||
cltver.grid(row = 0, column = 0, columnspan = 3, padx = 5, pady = 5, sticky = 'ew')
|
||||
cltipaddlbl.grid(row = 1, column = 0, padx = 5, pady = 5, sticky = 'e')
|
||||
self.cltipadd.grid(row = 1, column = 1, padx = 5, pady = 5, sticky = 'ew')
|
||||
@ -509,21 +615,29 @@ class KmsGui(tk.Tk):
|
||||
self.cltfile.grid(row = 6, column = 1, padx = 5, pady = 5, sticky = 'ew')
|
||||
cltfilebtnwin.grid(row = 6, column = 2, padx = 5, pady = 5, sticky = 'ew')
|
||||
self.chkcltfile.grid(row = 7, column = 1, padx = 5, pady = 5, sticky = 'ew')
|
||||
chkcltasy.grid(row = 7, column = 2, padx = 5, pady = 5, sticky = 'ew')
|
||||
cltlevellbl.grid(row = 8, column = 0, padx = 5, pady = 5, sticky = 'e')
|
||||
self.cltlevel.grid(row = 8, column = 1, padx = 5, pady = 5, sticky = 'ew')
|
||||
cltsizelbl.grid(row = 9, column = 0, padx = 5, pady = 5, sticky = 'e')
|
||||
self.cltsize.grid(row = 9, column = 1, padx = 5, pady = 5, sticky = 'ew')
|
||||
|
||||
# ugly fix when client-side mode is activated.
|
||||
templbl = tk.Label(self.pagewidgets["Clt"]["PageWin"]["PageStart"],
|
||||
bg = self.customcolors['lavender']).grid(row = 10, column = 0,
|
||||
padx = 35, pady = 54, sticky = 'e')
|
||||
|
||||
## Create widgets (optcltwin:Clt:PageWin:PageEnd) -------------------------------------------------------------------------------------------
|
||||
|
||||
## Layout widgets (optcltwin:Clt:PageWin:PageEnd)
|
||||
tk.Label(self.pagewidgets["Clt"]["PageWin"]["PageEnd"], width = 0, height = 0).grid(row = 0, column = 0, padx = 5, pady = 5, sticky = 'nw')
|
||||
# a label for vertical aligning with PageStart
|
||||
tk.Label(self.pagewidgets["Clt"]["PageWin"]["PageEnd"], width = 0,
|
||||
height = 0, bg = self.customcolors['lavender']).grid(row = 0, column = 0, padx = 5, pady = 5, sticky = 'nw')
|
||||
|
||||
# Store Clt widgets.
|
||||
self.storewidgets_clt = self.gui_store(side = "Clt", typewidgets = ['Button', 'Entry', 'TCombobox'])
|
||||
## Store client-side widgets.
|
||||
self.storewidgets_clt = self.gui_store(side = "Clt", typewidgets = ['Button', 'Entry', 'TCombobox', 'Checkbutton'])
|
||||
self.storewidgets_clt.append(self.chkcltfile)
|
||||
|
||||
# Create widgets and layout (msgcltwin) -----------------------------------------------------------------------------------------------------
|
||||
## Create widgets and layout (msgcltwin) -----------------------------------------------------------------------------------------------------
|
||||
self.textboxclt = TextDoubleScroll(self.msgcltwin, background = self.customcolors['black'], wrap = 'none', state = 'disabled',
|
||||
relief = 'ridge', font = self.msgfont)
|
||||
self.textboxclt.put()
|
||||
@ -540,22 +654,22 @@ class KmsGui(tk.Tk):
|
||||
# is a STRING.
|
||||
return value
|
||||
|
||||
def prep_logfile(self, filepath):
|
||||
def prep_logfile(self, filepath, status):
|
||||
# FILE (pretty on, log view off, logfile yes)
|
||||
# FILEOFF (pretty on, log view off, no logfile)
|
||||
# STDOUT (pretty off, log view on, no logfile)
|
||||
# STDOUTOFF (pretty off, log view off, logfile yes)
|
||||
# FILESTDOUT (pretty off, log view on, logfile yes)
|
||||
st = self.chksrvfile.state()
|
||||
if st == 'FILE':
|
||||
|
||||
if status == 'FILE':
|
||||
return filepath
|
||||
elif st in ['FILESTDOUT', 'STDOUTOFF']:
|
||||
return [st, filepath]
|
||||
elif st in ['STDOUT', 'FILEOFF']:
|
||||
return st
|
||||
elif status in ['FILESTDOUT', 'STDOUTOFF']:
|
||||
return [status, filepath]
|
||||
elif status in ['STDOUT', 'FILEOFF']:
|
||||
return status
|
||||
|
||||
def validate_int(self, value):
|
||||
return value == '' or value.isdigit()
|
||||
return value == "" or value.isdigit()
|
||||
|
||||
def validate_float(self, value):
|
||||
if value == "":
|
||||
@ -566,13 +680,13 @@ class KmsGui(tk.Tk):
|
||||
except ValueError:
|
||||
return False
|
||||
|
||||
def clt_on_show(self, force = False):
|
||||
if self.optcltwin.winfo_ismapped() or force:
|
||||
def clt_on_show(self, force_remove = False, force_view = False):
|
||||
if self.optcltwin.winfo_ismapped() or force_remove:
|
||||
self.shbtnclt['text'] = 'SHOW\nCLIENT'
|
||||
self.optcltwin.grid_remove()
|
||||
self.msgcltwin.grid_remove()
|
||||
self.btncltwin.place_forget()
|
||||
else:
|
||||
elif not self.optcltwin.winfo_ismapped() or force_view:
|
||||
self.shbtnclt['text'] = 'HIDE\nCLIENT'
|
||||
self.optcltwin.grid()
|
||||
self.msgcltwin.grid()
|
||||
@ -580,12 +694,12 @@ class KmsGui(tk.Tk):
|
||||
|
||||
def srv_on_start(self):
|
||||
if self.runbtnsrv['text'] == 'START\nSERVER':
|
||||
self.on_clear([txsrv, txclt])
|
||||
self.srv_actions_start()
|
||||
# wait for switch.
|
||||
while not serverthread.is_running_server:
|
||||
pass
|
||||
|
||||
self.on_clear([txsrv, txclt])
|
||||
self.srv_toggle_all(on_start = True)
|
||||
# run thread for interrupting server when an error happens.
|
||||
self.srv_eject_thread = threading.Thread(target = self.srv_eject, name = "Thread-SrvEjt")
|
||||
@ -609,12 +723,18 @@ class KmsGui(tk.Tk):
|
||||
srv_config[srv_options['count']['des']] = self.prep_option(self.count.get())
|
||||
srv_config[srv_options['activation']['des']] = self.prep_option(self.activ.get())
|
||||
srv_config[srv_options['renewal']['des']] = self.prep_option(self.renew.get())
|
||||
srv_config[srv_options['lfile']['des']] = self.prep_logfile(self.srvfile.get())
|
||||
srv_config[srv_options['lfile']['des']] = self.prep_logfile(self.srvfile.get(), self.chksrvfile.state())
|
||||
srv_config[srv_options['asyncmsg']['des']] = self.chkvalsrvasy.get()
|
||||
srv_config[srv_options['llevel']['des']] = self.srvlevel.get()
|
||||
srv_config[srv_options['sql']['des']] = self.chkvalsql.get()
|
||||
srv_config[srv_options['lsize']['des']] = self.prep_option(self.srvsize.get())
|
||||
srv_config[srv_options['time0']['des']] = self.prep_option(self.timeout0.get())
|
||||
|
||||
srv_config[srv_options['time0']['des']] = self.prep_option(self.timeout0.get())
|
||||
srv_config[srv_options['sql']['des']] = self.chkvalsql.get()
|
||||
|
||||
## Redirect stdout.
|
||||
gui_redirector('stdout', redirect_to = TextRedirect.Log,
|
||||
redirect_conditio = (srv_config[srv_options['lfile']['des']] in ['STDOUT', 'FILESTDOUT']))
|
||||
gui_redirector_setup()
|
||||
serverqueue.put('start')
|
||||
|
||||
def srv_actions_stop(self):
|
||||
@ -641,6 +761,8 @@ class KmsGui(tk.Tk):
|
||||
foreground = self.customcolors['white'])
|
||||
for widget in self.storewidgets_srv:
|
||||
widget.configure(state = 'normal')
|
||||
if isinstance(widget, ListboxOfRadiobuttons):
|
||||
widget.change()
|
||||
self.runbtnclt.configure(state = 'disabled')
|
||||
|
||||
def srv_toggle_state(self):
|
||||
@ -652,13 +774,18 @@ class KmsGui(tk.Tk):
|
||||
self.statesrv.configure(text = txt, foreground = color)
|
||||
|
||||
def clt_on_start(self):
|
||||
if self.onlyclt:
|
||||
self.on_clear([txclt])
|
||||
else:
|
||||
rng, add_newline = self.on_clear_setup()
|
||||
self.on_clear([txsrv, txclt], clear_range = [rng, None], newline_list = [add_newline, False])
|
||||
|
||||
self.clt_actions_start()
|
||||
# run thread for disabling interrupt server and client, when client running.
|
||||
self.clt_eject_thread = threading.Thread(target = self.clt_eject, name = "Thread-CltEjt")
|
||||
self.clt_eject_thread.setDaemon(True)
|
||||
self.clt_eject_thread.start()
|
||||
|
||||
self.on_clear([txsrv, txclt])
|
||||
for widget in self.storewidgets_clt + [self.runbtnsrv, self.runbtnclt]:
|
||||
widget.configure(state = 'disabled')
|
||||
|
||||
@ -668,10 +795,16 @@ class KmsGui(tk.Tk):
|
||||
clt_config[clt_options['mode']['des']] = self.cltmode.get()
|
||||
clt_config[clt_options['cmid']['des']] = self.cltcmid.get()
|
||||
clt_config[clt_options['name']['des']] = self.cltname.get()
|
||||
clt_config[clt_options['lfile']['des']] = self.prep_logfile(self.cltfile.get(), self.chkcltfile.state())
|
||||
clt_config[clt_options['asyncmsg']['des']] = self.chkvalcltasy.get()
|
||||
clt_config[clt_options['llevel']['des']] = self.cltlevel.get()
|
||||
clt_config[clt_options['lfile']['des']] = self.prep_logfile(self.cltfile.get())
|
||||
clt_config[clt_options['lsize']['des']] = self.prep_option(self.cltsize.get())
|
||||
|
||||
## Redirect stdout.
|
||||
gui_redirector('stdout', redirect_to = TextRedirect.Log,
|
||||
redirect_conditio = (clt_config[clt_options['lfile']['des']] in ['STDOUT', 'FILESTDOUT']))
|
||||
gui_redirector_setup()
|
||||
|
||||
# run client (in a thread).
|
||||
self.clientthread = client_thread(name = "Thread-Clt")
|
||||
self.clientthread.setDaemon(True)
|
||||
@ -681,8 +814,18 @@ class KmsGui(tk.Tk):
|
||||
def clt_eject(self):
|
||||
while self.clientthread.is_alive():
|
||||
sleep(0.1)
|
||||
for widget in self.storewidgets_clt + [self.runbtnsrv, self.runbtnclt]:
|
||||
widget.configure(state = 'normal')
|
||||
|
||||
widgets = self.storewidgets_clt + [self.runbtnclt]
|
||||
if not self.onlyclt:
|
||||
widgets += [self.runbtnsrv]
|
||||
|
||||
for widget in widgets:
|
||||
if isinstance(widget, ttk.Combobox):
|
||||
widget.configure(state = 'readonly')
|
||||
else:
|
||||
widget.configure(state = 'normal')
|
||||
if isinstance(widget, ListboxOfRadiobuttons):
|
||||
widget.change()
|
||||
|
||||
def on_exit(self):
|
||||
if serverthread.is_running_server:
|
||||
@ -693,8 +836,38 @@ class KmsGui(tk.Tk):
|
||||
server_terminate(serverthread, exit_thread = True)
|
||||
self.destroy()
|
||||
|
||||
def on_clear(self, widgetlist):
|
||||
for widget in widgetlist:
|
||||
def on_clear_setup(self):
|
||||
if any(opt in ['STDOUT', 'FILESTDOUT'] for opt in srv_config[srv_options['lfile']['des']]):
|
||||
if self.count_clear == 0:
|
||||
self.ini = txsrv.index('end')
|
||||
add_newline = False
|
||||
else:
|
||||
if self.count_clear == 1:
|
||||
self.ini = '%s.0' %(int(self.ini[0]) - 1)
|
||||
else:
|
||||
self.ini = '%s.0' %(int(self.ini[0]))
|
||||
add_newline = True
|
||||
rng = [self.ini, 'end']
|
||||
self.count_clear += 1
|
||||
else:
|
||||
rng, add_newline = None, False
|
||||
self.count_clear = 0
|
||||
|
||||
return rng, add_newline
|
||||
|
||||
def on_clear(self, widget_list, clear_range = None, newline_list = []):
|
||||
if newline_list == []:
|
||||
newline_list = len(widget_list) * [False]
|
||||
|
||||
for num, couple in enumerate(zip(widget_list, newline_list)):
|
||||
widget, add_n = couple
|
||||
try:
|
||||
ini, fin = clear_range[num]
|
||||
except TypeError:
|
||||
ini, fin = '1.0', 'end'
|
||||
|
||||
widget.configure(state = 'normal')
|
||||
widget.delete('1.0', 'end')
|
||||
widget.delete(ini, fin)
|
||||
if add_n:
|
||||
widget.insert('end', '\n')
|
||||
widget.configure(state = 'disabled')
|
||||
|
@ -120,38 +120,22 @@ class ToolTip(object):
|
||||
self.tw = None
|
||||
|
||||
##-----------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
# https://stackoverflow.com/questions/2914603/segmentation-fault-while-redirecting-sys-stdout-to-tkinter-text-widget
|
||||
# https://stackoverflow.com/questions/7217715/threadsafe-printing-across-multiple-processes-python-2-x
|
||||
# https://stackoverflow.com/questions/3029816/how-do-i-get-a-thread-safe-print-in-python-2-6
|
||||
# https://stackoverflow.com/questions/20303291/issue-with-redirecting-stdout-to-tkinter-text-widget-with-threads
|
||||
|
||||
class TextRedirect(object):
|
||||
class StdoutRedirect(object):
|
||||
tag_num = 0
|
||||
|
||||
class TextRedirect(object):
|
||||
class Pretty(object):
|
||||
grpmsg = unformat_message([MsgMap[1], MsgMap[7], MsgMap[12], MsgMap[20]])
|
||||
arrows = [ item[0] for item in grpmsg ]
|
||||
clt_msg_nonewline = [ item[1] for item in grpmsg ]
|
||||
arrows = list(set(arrows))
|
||||
lenarrow = len(arrows[0])
|
||||
srv_msg_nonewline = [ item[0] for item in unformat_message([MsgMap[2], MsgMap[5], MsgMap[13], MsgMap[18]]) ]
|
||||
terminator = unformat_message([MsgMap[21]])[0][0]
|
||||
msg_align = [ msg[0].replace('\t', '').replace('\n', '') for msg in unformat_message([MsgMap[-2], MsgMap[-4]])]
|
||||
newlinecut = [-1, -2, -4, -5]
|
||||
msg_align = [ msg[0].replace('\t', '').replace('\n', '') for msg in unformat_message([MsgMap[-2], MsgMap[-4]]) ]
|
||||
|
||||
def __init__(self, srv_text_space, clt_text_space, customcolors, str_to_print, where):
|
||||
def __init__(self, srv_text_space, clt_text_space, customcolors):
|
||||
self.srv_text_space = srv_text_space
|
||||
self.clt_text_space = clt_text_space
|
||||
self.customcolors = customcolors
|
||||
self.str_to_print = str_to_print
|
||||
self.where = where
|
||||
self.textbox_do()
|
||||
|
||||
def textbox_finish(self, message):
|
||||
if message == self.terminator:
|
||||
TextRedirect.StdoutRedirect.tag_num = 0
|
||||
TextRedirect.StdoutRedirect.newlinecut = [-1, -2, -4, -5]
|
||||
|
||||
def textbox_write(self, tag, message, color, extras):
|
||||
widget = self.textbox_choose(message)
|
||||
self.w_maxpix, self.h_maxpix = widget.winfo_width(), widget.winfo_height()
|
||||
@ -161,16 +145,17 @@ class TextRedirect(object):
|
||||
self.textbox_color(tag, widget, color, self.customcolors['black'], extras)
|
||||
widget.after(100, widget.see('end'))
|
||||
widget.configure(state = 'disabled')
|
||||
self.textbox_finish(message)
|
||||
|
||||
|
||||
def textbox_choose(self, message):
|
||||
if self.where == "srv":
|
||||
if any(item.startswith('logsrv') for item in [message, self.str_to_print]):
|
||||
self.srv_text_space.focus_set()
|
||||
self.where = "srv"
|
||||
return self.srv_text_space
|
||||
elif self.where == "clt":
|
||||
elif any(item.startswith('logclt') for item in [message, self.str_to_print]):
|
||||
self.clt_text_space.focus_set()
|
||||
self.where = "clt"
|
||||
return self.clt_text_space
|
||||
|
||||
|
||||
def textbox_color(self, tag, widget, forecolor = 'white', backcolor = 'black', extras = []):
|
||||
for extra in extras:
|
||||
if extra == 'bold':
|
||||
@ -211,8 +196,8 @@ class TextRedirect(object):
|
||||
# horizontal align.
|
||||
if msg_unformat in self.msg_align:
|
||||
msg_strip = message.lstrip('\n')
|
||||
message = '\n' * (len(message) - len(msg_strip) + TextRedirect.StdoutRedirect.newlinecut[0]) + msg_strip
|
||||
TextRedirect.StdoutRedirect.newlinecut.pop(0)
|
||||
message = '\n' * (len(message) - len(msg_strip) + TextRedirect.Pretty.newlinecut[0]) + msg_strip
|
||||
TextRedirect.Pretty.newlinecut.pop(0)
|
||||
|
||||
count = Counter(message)
|
||||
countab = (count['\t'] if count['\t'] != 0 else 1)
|
||||
@ -220,24 +205,48 @@ class TextRedirect(object):
|
||||
return message
|
||||
|
||||
def textbox_do(self):
|
||||
msgs, TextRedirect.StdoutRedirect.tag_num = unshell_message(self.str_to_print, TextRedirect.StdoutRedirect.tag_num)
|
||||
msgs, TextRedirect.Pretty.tag_num = unshell_message(self.str_to_print, TextRedirect.Pretty.tag_num)
|
||||
for tag in msgs:
|
||||
self.textbox_write(tag, msgs[tag]['text'], self.customcolors[msgs[tag]['color']], msgs[tag]['extra'])
|
||||
|
||||
class StderrRedirect(StdoutRedirect):
|
||||
def __init__(self, srv_text_space, clt_text_space, customcolors):
|
||||
|
||||
def flush(self):
|
||||
pass
|
||||
|
||||
def write(self, string):
|
||||
if string != '\n':
|
||||
self.str_to_print = string
|
||||
self.textbox_do()
|
||||
|
||||
class Stderr(Pretty):
|
||||
def __init__(self, srv_text_space, clt_text_space, customcolors, side):
|
||||
self.srv_text_space = srv_text_space
|
||||
self.clt_text_space = clt_text_space
|
||||
self.customcolors = customcolors
|
||||
self.side = side
|
||||
self.tag_err = 'STDERR'
|
||||
self.xfont = tkFont.Font(font = self.srv_text_space['font'])
|
||||
|
||||
def textbox_choose(self, message):
|
||||
if self.side == "srv":
|
||||
return self.srv_text_space
|
||||
elif self.side == "clt":
|
||||
return self.clt_text_space
|
||||
|
||||
def write(self, string):
|
||||
self.textbox_color(self.tag_err, self.srv_text_space, self.customcolors['red'], self.customcolors['black'])
|
||||
widget = self.textbox_choose(string)
|
||||
self.textbox_color(self.tag_err, widget, self.customcolors['red'], self.customcolors['black'])
|
||||
self.srv_text_space.configure(state = 'normal')
|
||||
self.srv_text_space.insert('end', string, self.tag_err)
|
||||
self.srv_text_space.see('end')
|
||||
self.srv_text_space.configure(state = 'disabled')
|
||||
|
||||
class Log(Pretty):
|
||||
def textbox_format(self, message):
|
||||
if message.startswith('logsrv'):
|
||||
message = message.replace('logsrv ', '')
|
||||
if message.startswith('logclt'):
|
||||
message = message.replace('logclt ', '')
|
||||
return message + '\n'
|
||||
|
||||
##-----------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
class TextDoubleScroll(tk.Frame):
|
||||
@ -328,7 +337,7 @@ def custom_background(window):
|
||||
widget.configure(background = window.customcolors['lavender'])
|
||||
|
||||
# Hide client.
|
||||
window.clt_on_show(force = True)
|
||||
window.clt_on_show(force_remove = True)
|
||||
# Show Gui.
|
||||
window.deiconify()
|
||||
|
||||
@ -488,9 +497,14 @@ class ListboxOfRadiobuttons(tk.Frame):
|
||||
if st in ['STDOUT', 'FILEOFF']:
|
||||
if wclass == 'Entry':
|
||||
widget.delete(0, 'end')
|
||||
widget.configure(state = "disabled")
|
||||
elif wclass == 'TCombobox':
|
||||
widget.set('')
|
||||
widget.configure(state = "disabled")
|
||||
if st == 'STDOUT':
|
||||
widget.set(default)
|
||||
widget.configure(state = "readonly")
|
||||
elif st == 'FILEOFF':
|
||||
widget.set('')
|
||||
widget.configure(state = "disabled")
|
||||
elif st in ['FILE', 'FILESTDOUT', 'STDOUTOFF']:
|
||||
if wclass == 'Entry':
|
||||
widget.configure(state = "normal")
|
||||
|
@ -86,28 +86,104 @@ class LevelFormatter(logging.Formatter):
|
||||
formatter = self.formatters.get(record.levelno, self.default_fmt)
|
||||
return formatter.format(record)
|
||||
|
||||
# based on https://github.com/jruere/multiprocessing-logging (license LGPL-3.0)
|
||||
from multiprocessing import Queue as MPQueue
|
||||
try:
|
||||
# Python 2.x imports
|
||||
import Queue as Queue
|
||||
except ImportError:
|
||||
# Python 3.x imports
|
||||
import queue as Queue
|
||||
import threading
|
||||
|
||||
class MultiProcessingLogHandler(logging.Handler):
|
||||
def __init__(self, name, handler = None):
|
||||
super(MultiProcessingLogHandler, self).__init__()
|
||||
self.queue = MPQueue(-1)
|
||||
if handler is None:
|
||||
handler = logging.StreamHandler()
|
||||
self.handler = handler
|
||||
self.name = handler.name
|
||||
|
||||
self.setLevel(self.handler.level)
|
||||
self.setFormatter(self.handler.formatter)
|
||||
self.filters = self.handler.filters
|
||||
|
||||
self.is_closed = False
|
||||
self.receive_thread = threading.Thread(target = self.receive, name = name)
|
||||
self.receive_thread.daemon = True
|
||||
self.receive_thread.start()
|
||||
|
||||
def setFormatter(self, fmt):
|
||||
super(MultiProcessingLogHandler, self).setFormatter(fmt)
|
||||
self.handler.setFormatter(fmt)
|
||||
|
||||
def emit(self, record):
|
||||
try:
|
||||
if record.args:
|
||||
record.msg = record.msg %record.args
|
||||
record.args = None
|
||||
if record.exc_info:
|
||||
dummy = self.format(record)
|
||||
record.exc_info = None
|
||||
self.queue.put_nowait(record)
|
||||
except (KeyboardInterrupt, SystemExit):
|
||||
raise
|
||||
except:
|
||||
self.handleError(record)
|
||||
|
||||
def receive(self):
|
||||
while not (self.is_closed and self.queue.empty()):
|
||||
try:
|
||||
record = self.queue.get(timeout = 0.2)
|
||||
self.handler.emit(record)
|
||||
except (KeyboardInterrupt, SystemExit):
|
||||
raise
|
||||
except EOFError:
|
||||
break
|
||||
except Queue.Empty:
|
||||
pass
|
||||
except:
|
||||
logging.exception('Error in log handler.')
|
||||
self.queue.close()
|
||||
self.queue.join_thread()
|
||||
|
||||
def close(self):
|
||||
if not self.is_closed:
|
||||
self.is_closed = True
|
||||
self.receive_thread.join(5.0)
|
||||
self.handler.close()
|
||||
super(MultiProcessingLogHandler, self).close()
|
||||
|
||||
|
||||
def logger_create(log_obj, config, mode = 'a'):
|
||||
# Create new level.
|
||||
add_logging_level('MINI', logging.CRITICAL + 10)
|
||||
log_handlers = []
|
||||
|
||||
# Configure visualization.
|
||||
log_handlers = []
|
||||
if any(opt in ['STDOUT', 'FILESTDOUT', 'STDOUTOFF'] for opt in config['logfile']):
|
||||
if 'STDOUTOFF' not in config['logfile']:
|
||||
# STDOUT.
|
||||
log_handlers.append(logging.StreamHandler(sys.stdout))
|
||||
if any(opt in ['STDOUT', 'FILESTDOUT'] for opt in config['logfile']):
|
||||
# STDOUT or FILESTDOUT.
|
||||
hand_stdout = logging.StreamHandler(sys.stdout)
|
||||
hand_stdout.name = 'LogStdout'
|
||||
log_handlers.append(hand_stdout)
|
||||
if any(opt in ['STDOUTOFF', 'FILESTDOUT'] for opt in config['logfile']):
|
||||
# FILESTDOUT or STDOUTOFF.
|
||||
log_handlers.append(RotatingFileHandler(filename = config['logfile'][1], mode = mode, maxBytes = int(config['logsize'] * 1024 * 512),
|
||||
backupCount = 1, encoding = None, delay = 0))
|
||||
# STDOUTOFF or FILESTDOUT.
|
||||
hand_rotate = RotatingFileHandler(filename = config['logfile'][1], mode = mode, maxBytes = int(config['logsize'] * 1024 * 512),
|
||||
backupCount = 1, encoding = None, delay = 0)
|
||||
hand_rotate.name = 'LogRotate'
|
||||
log_handlers.append(hand_rotate)
|
||||
elif 'FILEOFF' in config['logfile']:
|
||||
config['loglevel'] = 'ERROR' # for py-kms GUI: set a recognized level never used.
|
||||
log_handlers.append(logging.FileHandler(os.devnull))
|
||||
hand_null = logging.FileHandler(os.devnull)
|
||||
hand_null.name = 'LogNull'
|
||||
log_handlers.append(hand_null)
|
||||
else:
|
||||
# FILE.
|
||||
log_handlers.append(RotatingFileHandler(filename = config['logfile'][0], mode = mode, maxBytes = int(config['logsize'] * 1024 * 512),
|
||||
backupCount = 1, encoding = None, delay = 0))
|
||||
hand_rotate = RotatingFileHandler(filename = config['logfile'][0], mode = mode, maxBytes = int(config['logsize'] * 1024 * 512),
|
||||
backupCount = 1, encoding = None, delay = 0)
|
||||
hand_rotate.name = 'LogRotate'
|
||||
log_handlers.append(hand_rotate)
|
||||
|
||||
# Configure formattation.
|
||||
try:
|
||||
@ -115,27 +191,38 @@ def logger_create(log_obj, config, mode = 'a'):
|
||||
except AttributeError:
|
||||
levelnames = logging._levelNames
|
||||
levelnum = [k for k in levelnames if k != 0]
|
||||
frmt0 = '%(asctime)s %(levelname)-8s %(message)s'
|
||||
frmt1 = '[%(asctime)s] [%(levelname)-8s] %(host)s %(status)s %(product)s %(message)s'
|
||||
levelformdict = {}
|
||||
for num in levelnum:
|
||||
if num != logging.CRITICAL + 10:
|
||||
levelformdict[num] = frmt0
|
||||
else:
|
||||
levelformdict[num] = frmt1
|
||||
|
||||
# Set level and format.
|
||||
levelformdictcopy = levelformdict.copy()
|
||||
frmt_gen = '%(asctime)s %(levelname)-8s %(message)s'
|
||||
frmt_std = '%(name)s %(asctime)s %(levelname)-8s %(message)s'
|
||||
frmt_min = '[%(asctime)s] [%(levelname)-8s] %(host)s %(status)s %(product)s %(message)s'
|
||||
|
||||
def apply_formatter(levelnum, formats, handler, color = False):
|
||||
levelformdict = {}
|
||||
for num in levelnum:
|
||||
if num != logging.CRITICAL + 10:
|
||||
levelformdict[num] = formats[0]
|
||||
else:
|
||||
levelformdict[num] = formats[1]
|
||||
handler.setFormatter(LevelFormatter(levelformdict, color = color))
|
||||
return handler
|
||||
|
||||
# Clear old handlers.
|
||||
if log_obj.handlers:
|
||||
log_obj.handlers = []
|
||||
|
||||
for log_handler in log_handlers:
|
||||
log_handler.setLevel(config['loglevel'])
|
||||
if log_handler.__class__.__name__ == 'StreamHandler':
|
||||
log_handler.setFormatter(LevelFormatter(levelformdict, color = True))
|
||||
elif log_handler.__class__.__name__ == 'RotatingFileHandler':
|
||||
log_handler.setFormatter(LevelFormatter(levelformdictcopy, color = False))
|
||||
if log_handler.name in ['LogStdout']:
|
||||
log_handler = apply_formatter(levelnum, (frmt_std, frmt_min), log_handler, color = True)
|
||||
elif log_handler.name in ['LogRotate']:
|
||||
log_handler = apply_formatter(levelnum, (frmt_gen, frmt_min), log_handler)
|
||||
# Attach.
|
||||
if config['asyncmsg']:
|
||||
log_obj.addHandler(MultiProcessingLogHandler('Thread-AsyncMsg{0}'.format(log_handler.name), handler = log_handler))
|
||||
else:
|
||||
log_obj.addHandler(log_handler)
|
||||
|
||||
# Attach.
|
||||
log_obj.setLevel(config['loglevel'])
|
||||
[ log_obj.addHandler(log_handler) for log_handler in log_handlers ]
|
||||
|
||||
#------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
@ -144,14 +231,16 @@ def check_logfile(optionlog, defaultlog, where):
|
||||
optionlog = [optionlog]
|
||||
|
||||
lenopt = len(optionlog)
|
||||
msg_dir = "{reverse}{red}{bold}argument logfile: invalid directory: '%s'. Exiting...{end}"
|
||||
msg_long = "{reverse}{red}{bold}argument logfile: too much arguments. Exiting...{end}"
|
||||
msg_log = "{reverse}{red}{bold}argument logfile: not a log file, invalid extension: '%s'. Exiting...{end}"
|
||||
msg_dir = "{reverse}{red}{bold}argument `-F/--logfile`: invalid directory: '%s'. Exiting...{end}"
|
||||
msg_long = "{reverse}{red}{bold}argument `-F/--logfile`: too much arguments. Exiting...{end}"
|
||||
msg_log = "{reverse}{red}{bold}argument `-F/--logfile`: not a log file, invalid extension: '%s'. Exiting...{end}"
|
||||
|
||||
def checkdir(path):
|
||||
filename = os.path.basename(path)
|
||||
pathname = os.path.dirname(path)
|
||||
if not os.path.isdir(pathname):
|
||||
if path.count('/') == 0:
|
||||
pathname = filename
|
||||
pretty_printer(put_text = msg_dir %pathname, where = where, to_exit = True)
|
||||
elif not filename.lower().endswith('.log'):
|
||||
pretty_printer(put_text = msg_log %filename, where = where, to_exit = True)
|
||||
@ -159,7 +248,6 @@ def check_logfile(optionlog, defaultlog, where):
|
||||
if lenopt > 2:
|
||||
pretty_printer(put_text = msg_long, where = where, to_exit = True)
|
||||
|
||||
|
||||
if (any(opt in ['FILESTDOUT', 'STDOUTOFF'] for opt in optionlog)):
|
||||
if lenopt == 1:
|
||||
# add default logfile.
|
||||
@ -215,8 +303,7 @@ def check_lcid(lcid, log_obj):
|
||||
fixlcid = next(k for k, v in locale.windows_locale.items() if v == locale.getdefaultlocale()[0])
|
||||
except StopIteration:
|
||||
fixlcid = 1033
|
||||
pretty_printer(log_obj = log_obj,
|
||||
put_text = "{reverse}{yellow}{bold}LCID %s auto-fixed with LCID %s{end}" %(lcid, fixlcid))
|
||||
pretty_printer(log_obj = log_obj, put_text = "{reverse}{yellow}{bold}LCID '%s' auto-fixed with LCID '%s'{end}" %(lcid, fixlcid))
|
||||
return fixlcid
|
||||
return lcid
|
||||
|
||||
@ -262,28 +349,123 @@ class KmsParserHelp(object):
|
||||
print(parser_base.epilog + '\n')
|
||||
parser_base.exit()
|
||||
|
||||
def kms_parser_get(parser):
|
||||
zeroarg, onearg = ([] for _ in range(2))
|
||||
act = vars(parser)['_actions']
|
||||
for i in range(len(act)):
|
||||
if act[i].option_strings not in ([], ['-h', '--help']):
|
||||
if isinstance(act[i], argparse._StoreAction):
|
||||
onearg.append(act[i].option_strings)
|
||||
else:
|
||||
zeroarg.append(act[i].option_strings)
|
||||
return zeroarg, onearg
|
||||
|
||||
def kms_parser_check_optionals(userarg, zeroarg, onearg, msg = 'optional py-kms server', exclude_opt_len = []):
|
||||
"""
|
||||
For optionals arguments:
|
||||
Don't allow duplicates,
|
||||
Don't allow abbreviations,
|
||||
Don't allow joining and not existing arguments,
|
||||
Checks length values passed to arguments.
|
||||
"""
|
||||
zeroarg = [item for sublist in zeroarg for item in sublist]
|
||||
onearg = [item for sublist in onearg for item in sublist]
|
||||
allarg = zeroarg + onearg
|
||||
|
||||
def is_abbrev(allarg, arg_to_check):
|
||||
for opt in allarg:
|
||||
if len(opt) > 2 and opt[2] == arg_to_check[2]:
|
||||
for indx in range(-1, -len(opt), -1):
|
||||
if opt[:indx] == arg_to_check:
|
||||
raise KmsParserException("%s argument `%s` abbreviation not allowed for `%s`" %(msg, arg_to_check, opt))
|
||||
return False
|
||||
|
||||
# Check abbreviations, joining, not existing.
|
||||
for arg in userarg:
|
||||
if arg not in allarg:
|
||||
if arg.startswith('-'):
|
||||
if arg == '--' or arg[:2] != '--' or not is_abbrev(allarg, arg):
|
||||
raise KmsParserException("unrecognized %s arguments: `%s`" %(msg, arg))
|
||||
|
||||
# Check duplicates.
|
||||
founds = [i for i in userarg if i in allarg]
|
||||
dup = [item for item in set(founds) if founds.count(item) > 1]
|
||||
if dup != []:
|
||||
raise KmsParserException("%s argument `%s` appears several times" %(msg, ', '.join(dup)))
|
||||
|
||||
# Check length.
|
||||
elem = None
|
||||
for found in founds:
|
||||
if found not in exclude_opt_len:
|
||||
pos = userarg.index(found)
|
||||
try:
|
||||
if found in zeroarg:
|
||||
elem = userarg[pos + 1]
|
||||
num = "zero arguments,"
|
||||
elif found in onearg:
|
||||
elem = userarg[pos + 2]
|
||||
num = "one argument,"
|
||||
except IndexError:
|
||||
pass
|
||||
|
||||
if elem and elem not in allarg:
|
||||
raise KmsParserException("%s argument `" %msg + found + "`:" + " expected " + num + " unrecognized: '%s'" %elem)
|
||||
|
||||
def kms_parser_check_positionals(config, parse_method, arguments = None, msg = 'positional py-kms server'):
|
||||
try:
|
||||
if arguments:
|
||||
config.update(vars(parse_method(arguments)))
|
||||
else:
|
||||
config.update(vars(parse_method()))
|
||||
except KmsParserException as e:
|
||||
e = str(e)
|
||||
if e.startswith('argument'):
|
||||
raise
|
||||
else:
|
||||
raise KmsParserException("unrecognized %s arguments: '%s'" %(msg, e.split(': ')[1]))
|
||||
|
||||
#------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
def proper_none(dictionary):
|
||||
for key in dictionary.keys():
|
||||
dictionary[key] = None if dictionary[key] == 'None' else dictionary[key]
|
||||
|
||||
def check_setup(config, options, logger, where):
|
||||
# 'None'--> None.
|
||||
proper_none(config)
|
||||
|
||||
# Check logfile.
|
||||
config['logfile'] = check_logfile(config['logfile'], options['lfile']['def'], where = where)
|
||||
|
||||
# Setup hidden or not messages.
|
||||
# Check logsize (py-kms Gui).
|
||||
if config['logsize'] == "":
|
||||
if any(opt in ['STDOUT', 'FILEOFF'] for opt in config['logfile']):
|
||||
# set a recognized size never used.
|
||||
config['logsize'] = 0
|
||||
else:
|
||||
pretty_printer(put_text = "{reverse}{red}{bold}argument `-S/--logsize`: invalid with: '%s'. Exiting...{end}" %config['logsize'],
|
||||
where = where, to_exit = True)
|
||||
|
||||
# Check loglevel (py-kms Gui).
|
||||
if config['loglevel'] == "":
|
||||
# set a recognized level never used.
|
||||
config['loglevel'] = 'ERROR'
|
||||
|
||||
# Setup hidden / asynchronous messages.
|
||||
hidden = ['STDOUT', 'FILESTDOUT', 'STDOUTOFF']
|
||||
ShellMessage.view = (False if any(opt in hidden for opt in config['logfile']) else True)
|
||||
view_flag = (False if any(opt in hidden for opt in config['logfile']) else True)
|
||||
if where == 'srv':
|
||||
ShellMessage.viewsrv = view_flag
|
||||
ShellMessage.asyncmsgsrv = config['asyncmsg']
|
||||
elif where == 'clt':
|
||||
ShellMessage.viewclt = view_flag
|
||||
ShellMessage.asyncmsgclt = config['asyncmsg']
|
||||
|
||||
# Create log.
|
||||
logger_create(logger, config, mode = 'a')
|
||||
|
||||
# 'None'--> None.
|
||||
proper_none(config)
|
||||
|
||||
# Check port.
|
||||
if not 1 <= config['port'] <= 65535:
|
||||
pretty_printer(log_obj = logger.error, to_exit = True,
|
||||
if (config['port'] == "") or (not 1 <= config['port'] <= 65535):
|
||||
pretty_printer(log_obj = logger.error, where = where, to_exit = True,
|
||||
put_text = "{reverse}{red}{bold}Port number '%s' is invalid. Enter between 1 - 65535. Exiting...{end}" %config['port'])
|
||||
|
||||
#------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
@ -29,6 +29,7 @@ from pykms_RpcBase import rpcBase
|
||||
from pykms_Dcerpc import MSRPCHeader
|
||||
from pykms_Misc import check_setup, check_lcid
|
||||
from pykms_Misc import KmsParser, KmsParserException, KmsParserHelp
|
||||
from pykms_Misc import kms_parser_get, kms_parser_check_optionals, kms_parser_check_positionals
|
||||
from pykms_Format import enco, deco, pretty_printer
|
||||
from Etrigan import Etrigan, Etrigan_parser, Etrigan_check, Etrigan_job
|
||||
|
||||
@ -148,12 +149,15 @@ class server_thread(threading.Thread):
|
||||
# Create and run server.
|
||||
self.server = server_create()
|
||||
self.server.pykms_serve()
|
||||
except SystemExit as e:
|
||||
except (SystemExit, Exception) as e:
|
||||
self.eject = True
|
||||
if not self.with_gui:
|
||||
raise
|
||||
else:
|
||||
continue
|
||||
if isinstance(e, SystemExit):
|
||||
continue
|
||||
else:
|
||||
raise
|
||||
|
||||
##---------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
@ -168,17 +172,19 @@ srv_options = {
|
||||
'lcid' : {'help' : 'Use this option to manually specify an LCID for use with randomly generated ePIDs. Default is \"1033\" (en-us)',
|
||||
'def' : 1033, 'des' : "lcid"},
|
||||
'count' : {'help' : 'Use this option to specify the current client count. A number >=25 is required to enable activation of client OSes; \
|
||||
for server OSes and Office >=5', 'def' : None, 'des' : "CurrentClientCount"},
|
||||
for server OSes and Office >=5', 'def' : None, 'des' : "clientcount"},
|
||||
'activation' : {'help' : 'Use this option to specify the activation interval (in minutes). Default is \"120\" minutes (2 hours).',
|
||||
'def' : 120, 'des': "VLActivationInterval"},
|
||||
'def' : 120, 'des': "activation"},
|
||||
'renewal' : {'help' : 'Use this option to specify the renewal interval (in minutes). Default is \"10080\" minutes (7 days).',
|
||||
'def' : 1440 * 7, 'des' : "VLRenewalInterval"},
|
||||
'def' : 1440 * 7, 'des' : "renewal"},
|
||||
'sql' : {'help' : 'Use this option to store request information from unique clients in an SQLite database. Desactivated by default.',
|
||||
'def' : False, 'des' : "sqlite"},
|
||||
'hwid' : {'help' : 'Use this option to specify a HWID. The HWID must be an 16-character string of hex characters. \
|
||||
The default is \"364F463A8863D35F\" or type \"RANDOM\" to auto generate the HWID.', 'def' : "364F463A8863D35F", 'des' : "hwid"},
|
||||
'time0' : {'help' : 'Maximum inactivity time (in seconds) after which the connection with the client is closed. If \"None\" (default) serve forever.',
|
||||
'def' : None, 'des' : "timeout_idle"},
|
||||
'def' : None, 'des' : "timeoutidle"},
|
||||
'asyncmsg' : {'help' : 'Prints pretty / logging messages asynchronously. Desactivated by default.',
|
||||
'def' : False, 'des' : "asyncmsg"},
|
||||
'llevel' : {'help' : 'Use this option to set a log level. The default is \"ERROR\".', 'def' : "ERROR", 'des' : "loglevel",
|
||||
'choi' : ["CRITICAL", "ERROR", "WARNING", "INFO", "DEBUG", "MINI"]},
|
||||
'lfile' : {'help' : 'Use this option to set an output log file. The default is \"pykms_logserver.log\". \
|
||||
@ -189,11 +195,7 @@ Use \"STDOUTOFF\" to disable stdout messages. Use \"FILEOFF\" if you not want to
|
||||
}
|
||||
|
||||
def server_options():
|
||||
try:
|
||||
server_parser = KmsParser(description = srv_description, epilog = 'version: ' + srv_version, add_help = False, allow_abbrev = False)
|
||||
except TypeError:
|
||||
server_parser = KmsParser(description = srv_description, epilog = 'version: ' + srv_version, add_help = False)
|
||||
|
||||
server_parser = KmsParser(description = srv_description, epilog = 'version: ' + srv_version, add_help = False)
|
||||
server_parser.add_argument("ip", nargs = "?", action = "store", default = srv_options['ip']['def'], help = srv_options['ip']['help'], type = str)
|
||||
server_parser.add_argument("port", nargs = "?", action = "store", default = srv_options['port']['def'], help = srv_options['port']['help'], type = int)
|
||||
server_parser.add_argument("-e", "--epid", action = "store", dest = srv_options['epid']['des'], default = srv_options['epid']['def'],
|
||||
@ -204,85 +206,114 @@ def server_options():
|
||||
help = srv_options['count']['help'], type = str)
|
||||
server_parser.add_argument("-a", "--activation-interval", action = "store", dest = srv_options['activation']['des'],
|
||||
default = srv_options['activation']['def'], help = srv_options['activation']['help'], type = int)
|
||||
server_parser.add_argument("-r", "--renewal-interval", action = "store", dest = srv_options['renewal']['des'], default = srv_options['renewal']['def'],
|
||||
help = srv_options['renewal']['help'], type = int)
|
||||
server_parser.add_argument("-s", "--sqlite", action = "store_const", dest = srv_options['sql']['des'], const = True, default = srv_options['sql']['def'],
|
||||
help = srv_options['sql']['help'])
|
||||
server_parser.add_argument("-r", "--renewal-interval", action = "store", dest = srv_options['renewal']['des'],
|
||||
default = srv_options['renewal']['def'], help = srv_options['renewal']['help'], type = int)
|
||||
server_parser.add_argument("-s", "--sqlite", action = "store_true", dest = srv_options['sql']['des'],
|
||||
default = srv_options['sql']['def'], help = srv_options['sql']['help'])
|
||||
server_parser.add_argument("-w", "--hwid", action = "store", dest = srv_options['hwid']['des'], default = srv_options['hwid']['def'],
|
||||
help = srv_options['hwid']['help'], type = str)
|
||||
server_parser.add_argument("-t0", "--timeout-idle", action = "store", dest = srv_options['time0']['des'], default = srv_options['time0']['def'],
|
||||
help = srv_options['time0']['help'], type = str)
|
||||
server_parser.add_argument("-y", "--async-msg", action = "store_true", dest = srv_options['asyncmsg']['des'],
|
||||
default = srv_options['asyncmsg']['def'], help = srv_options['asyncmsg']['help'])
|
||||
server_parser.add_argument("-V", "--loglevel", action = "store", dest = srv_options['llevel']['des'], choices = srv_options['llevel']['choi'],
|
||||
default = srv_options['llevel']['def'], help = srv_options['llevel']['help'], type = str)
|
||||
server_parser.add_argument("-F", "--logfile", nargs = "+", action = "store", dest = srv_options['lfile']['des'], default = srv_options['lfile']['def'],
|
||||
help = srv_options['lfile']['help'], type = str)
|
||||
server_parser.add_argument("-F", "--logfile", nargs = "+", action = "store", dest = srv_options['lfile']['des'],
|
||||
default = srv_options['lfile']['def'], help = srv_options['lfile']['help'], type = str)
|
||||
server_parser.add_argument("-S", "--logsize", action = "store", dest = srv_options['lsize']['des'], default = srv_options['lsize']['def'],
|
||||
help = srv_options['lsize']['help'], type = float)
|
||||
|
||||
server_parser.add_argument("-h", "--help", action = "help", help = "show this help message and exit")
|
||||
|
||||
try:
|
||||
daemon_parser = KmsParser(description = "daemon options inherited from Etrigan", add_help = False, allow_abbrev = False)
|
||||
except TypeError:
|
||||
daemon_parser = KmsParser(description = "daemon options inherited from Etrigan", add_help = False)
|
||||
|
||||
daemon_parser = KmsParser(description = "daemon options inherited from Etrigan", add_help = False)
|
||||
daemon_subparser = daemon_parser.add_subparsers(dest = "mode")
|
||||
try:
|
||||
etrigan_parser = daemon_subparser.add_parser("etrigan", add_help = False, allow_abbrev = False)
|
||||
except TypeError:
|
||||
etrigan_parser = daemon_subparser.add_parser("etrigan", add_help = False)
|
||||
|
||||
etrigan_parser = daemon_subparser.add_parser("etrigan", add_help = False)
|
||||
etrigan_parser.add_argument("-g", "--gui", action = "store_const", dest = 'gui', const = True, default = False,
|
||||
help = "Enable py-kms GUI usage.")
|
||||
etrigan_parser = Etrigan_parser(parser = etrigan_parser)
|
||||
|
||||
try:
|
||||
if "-h" in sys.argv[1:]:
|
||||
userarg = sys.argv[1:]
|
||||
|
||||
# Run help.
|
||||
if any(arg in ["-h", "--help"] for arg in userarg):
|
||||
KmsParserHelp().printer(parsers = [server_parser, daemon_parser, etrigan_parser])
|
||||
|
||||
# Set defaults for config.
|
||||
# case: python3 pykms_Server.py
|
||||
srv_config.update(vars(server_parser.parse_args([])))
|
||||
# Eventually set daemon values for config.
|
||||
if 'etrigan' in sys.argv[1:]:
|
||||
if 'etrigan' == sys.argv[1]:
|
||||
# case: python3 pykms_Server.py etrigan start --daemon_optionals
|
||||
srv_config.update(vars(daemon_parser.parse_args(sys.argv[1:])))
|
||||
elif 'etrigan' == sys.argv[2]:
|
||||
# case: python3 pykms_Server.py 1.2.3.4 etrigan start --daemon_optionals
|
||||
srv_config['ip'] = sys.argv[1]
|
||||
srv_config.update(vars(daemon_parser.parse_args(sys.argv[2:])))
|
||||
else:
|
||||
# case: python3 pykms_Server.py 1.2.3.4 1234 --main_optionals etrigan start --daemon_optionals
|
||||
knw_args, knw_extras = server_parser.parse_known_args()
|
||||
# fix for logfile option (at the end) that catchs etrigan parser options.
|
||||
if 'etrigan' in knw_args.logfile:
|
||||
indx = knw_args.logfile.index('etrigan')
|
||||
for num, elem in enumerate(knw_args.logfile[indx:]):
|
||||
knw_extras.insert(num, elem)
|
||||
knw_args.logfile = knw_args.logfile[:indx]
|
||||
# Get stored arguments.
|
||||
pykmssrv_zeroarg, pykmssrv_onearg = kms_parser_get(server_parser)
|
||||
etrigan_zeroarg, etrigan_onearg = kms_parser_get(etrigan_parser)
|
||||
pykmssrv_zeroarg += ['etrigan'] # add subparser
|
||||
|
||||
# continue parsing.
|
||||
if len(knw_extras) > 0 and knw_extras[0] in ['etrigan']:
|
||||
daemon_parser.parse_args(knw_extras, namespace = knw_args)
|
||||
srv_config.update(vars(knw_args))
|
||||
else:
|
||||
# Update dict config.
|
||||
# case: python3 pykms_Server.py 1.2.3.4 1234 --main_optionals
|
||||
knw_args, knw_extras = server_parser.parse_known_args()
|
||||
if knw_extras != []:
|
||||
raise KmsParserException("unrecognized arguments: %s" %' '.join(knw_extras))
|
||||
else:
|
||||
srv_config.update(vars(knw_args))
|
||||
# Set defaults for config.
|
||||
# example case:
|
||||
# python3 pykms_Server.py
|
||||
srv_config.update(vars(server_parser.parse_args([])))
|
||||
|
||||
try:
|
||||
# Eventually set daemon options for dict server config.
|
||||
pos = sys.argv[1:].index('etrigan')
|
||||
# example cases:
|
||||
# python3 pykms_Server.py etrigan start
|
||||
# python3 pykms_Server.py etrigan start --daemon_optionals
|
||||
# python3 pykms_Server.py 1.2.3.4 etrigan start
|
||||
# python3 pykms_Server.py 1.2.3.4 etrigan start --daemon_optionals
|
||||
# python3 pykms_Server.py 1.2.3.4 1234 etrigan start
|
||||
# python3 pykms_Server.py 1.2.3.4 1234 etrigan start --daemon_optionals
|
||||
# python3 pykms_Server.py --pykms_optionals etrigan start
|
||||
# python3 pykms_Server.py --pykms_optionals etrigan start --daemon_optionals
|
||||
# python3 pykms_Server.py 1.2.3.4 --pykms_optionals etrigan start
|
||||
# python3 pykms_Server.py 1.2.3.4 --pykms_optionals etrigan start --daemon_optionals
|
||||
# python3 pykms_Server.py 1.2.3.4 1234 --pykms_optionals etrigan start
|
||||
# python3 pykms_Server.py 1.2.3.4 1234 --pykms_optionals etrigan start --daemon_optionals
|
||||
|
||||
kms_parser_check_optionals(userarg[0:pos], pykmssrv_zeroarg, pykmssrv_onearg, exclude_opt_len = ['-F', '--logfile'])
|
||||
kms_parser_check_positionals(srv_config, server_parser.parse_args, arguments = userarg[0:pos])
|
||||
kms_parser_check_optionals(userarg[pos:], etrigan_zeroarg, etrigan_onearg, msg = 'optional etrigan')
|
||||
kms_parser_check_positionals(srv_config, daemon_parser.parse_args, arguments = userarg[pos:], msg = 'positional etrigan')
|
||||
|
||||
except ValueError:
|
||||
# Update pykms options for dict server config.
|
||||
# example cases:
|
||||
# python3 pykms_Server.py 1.2.3.4
|
||||
# python3 pykms_Server.py 1.2.3.4 --pykms_optionals
|
||||
# python3 pykms_Server.py 1.2.3.4 1234
|
||||
# python3 pykms_Server.py 1.2.3.4 1234 --pykms_optionals
|
||||
# python3 pykms_Server.py --pykms_optionals
|
||||
|
||||
kms_parser_check_optionals(userarg, pykmssrv_zeroarg, pykmssrv_onearg, exclude_opt_len = ['-F', '--logfile'])
|
||||
kms_parser_check_positionals(srv_config, server_parser.parse_args)
|
||||
|
||||
except KmsParserException as e:
|
||||
pretty_printer(put_text = "{reverse}{red}{bold}%s. Exiting...{end}" %str(e), to_exit = True)
|
||||
|
||||
|
||||
class Etrigan_Check(Etrigan_check):
|
||||
def emit_opt_err(self, msg):
|
||||
pretty_printer(put_text = "{reverse}{red}{bold}%s{end}" %msg, to_exit = True)
|
||||
|
||||
class Etrigan(Etrigan):
|
||||
def emit_message(self, message, to_exit = False):
|
||||
if not self.mute:
|
||||
pretty_printer(put_text = "{reverse}{green}{bold}%s{end}" %message)
|
||||
if to_exit:
|
||||
sys.exit(0)
|
||||
|
||||
def emit_error(self, message, to_exit = True):
|
||||
if not self.mute:
|
||||
pretty_printer(put_text = "{reverse}{red}{bold}%s{end}" %message, to_exit = True)
|
||||
|
||||
def server_daemon():
|
||||
if 'etrigan' in srv_config.values():
|
||||
path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'pykms_config.pickle')
|
||||
|
||||
if srv_config['operation'] in ['stop', 'restart', 'status'] and len(sys.argv[1:]) > 2:
|
||||
pretty_printer(put_text = "{reverse}{red}{bold}too much arguments. Exiting...{end}", to_exit = True)
|
||||
pretty_printer(put_text = "{reverse}{red}{bold}too much arguments with etrigan '%s'. Exiting...{end}" %srv_config['operation'],
|
||||
to_exit = True)
|
||||
|
||||
# Check file arguments.
|
||||
Etrigan_Check().checkfile(srv_config['etriganpid'], '--etrigan-pid', '.pid')
|
||||
Etrigan_Check().checkfile(srv_config['etriganlog'], '--etrigan-log', '.log')
|
||||
|
||||
if srv_config['gui']:
|
||||
pass
|
||||
@ -357,21 +388,30 @@ def server_check():
|
||||
else:
|
||||
srv_config['dbSupport'] = True
|
||||
|
||||
# Check client count, timeout.
|
||||
list_dest = ['CurrentClientCount', 'timeout_idle']
|
||||
list_opt = ['--client-count', '--timeout-idle']
|
||||
|
||||
# Check other specific server options.
|
||||
list_dest = ['clientcount', 'timeoutidle']
|
||||
list_opt = ['-c/--client-count', '-t0/--timeout-idle']
|
||||
|
||||
if serverthread.with_gui:
|
||||
list_dest += ['activation', 'renewal']
|
||||
list_opt += ['-a/--activation-interval', '-r/--renewal-interval']
|
||||
|
||||
for dest, opt in zip(list_dest, list_opt):
|
||||
if srv_config[dest] is not None:
|
||||
if not srv_config[dest].isdigit():
|
||||
pretty_printer(log_obj = loggersrv.error, to_exit = True,
|
||||
put_text = "{reverse}{red}{bold}Option %s is invalid with '%s'. Exiting...{end}"
|
||||
%(opt, srv_config[dest]))
|
||||
else:
|
||||
srv_config[dest] = int(srv_config[dest])
|
||||
value = srv_config[dest]
|
||||
if (value is not None) and (not isinstance(value, int)):
|
||||
pretty_printer(log_obj = loggersrv.error, to_exit = True,
|
||||
put_text = "{reverse}{red}{bold}argument `%s`: invalid with: '%s'. Exiting...{end}" %(opt, value))
|
||||
|
||||
def server_create():
|
||||
server = KeyServer((srv_config['ip'], srv_config['port']), kmsServerHandler)
|
||||
server.timeout = srv_config['timeout_idle']
|
||||
try:
|
||||
server = KeyServer((srv_config['ip'], srv_config['port']), kmsServerHandler)
|
||||
except (socket.gaierror, socket.error) as e:
|
||||
pretty_printer(log_obj = loggersrv.error, to_exit = True,
|
||||
put_text = "{reverse}{red}{bold}Connection failed '%s:%d': %s. Exiting...{end}" %(srv_config['ip'],
|
||||
srv_config['port'],
|
||||
str(e)))
|
||||
server.timeout = srv_config['timeoutidle']
|
||||
loggersrv.info("TCP server listening at %s on port %d." % (srv_config['ip'], srv_config['port']))
|
||||
loggersrv.info("HWID: %s" % deco(binascii.b2a_hex(srv_config['hwid']), 'utf-8').upper())
|
||||
return server
|
||||
@ -426,20 +466,8 @@ def server_main_terminal():
|
||||
def server_with_gui():
|
||||
import pykms_GuiBase
|
||||
|
||||
width = 950
|
||||
height = 660
|
||||
|
||||
root = pykms_GuiBase.KmsGui()
|
||||
root.title(pykms_GuiBase.gui_description + ' (' + pykms_GuiBase.gui_version + ')')
|
||||
# Main window initial position.
|
||||
## https://stackoverflow.com/questions/14910858/how-to-specify-where-a-tkinter-window-opens
|
||||
ws = root.winfo_screenwidth()
|
||||
hs = root.winfo_screenheight()
|
||||
x = (ws / 2) - (width / 2)
|
||||
y = (hs / 2) - (height / 2)
|
||||
root.geometry('+%d+%d' %(x, y))
|
||||
# disable maximize button.
|
||||
root.resizable(0, 0)
|
||||
root.mainloop()
|
||||
|
||||
def server_main_no_terminal():
|
||||
@ -449,7 +477,7 @@ def server_main_no_terminal():
|
||||
|
||||
class kmsServerHandler(socketserver.BaseRequestHandler):
|
||||
def setup(self):
|
||||
loggersrv.info("Connection accepted: %s:%d" % (self.client_address[0], self.client_address[1]))
|
||||
loggersrv.info("Connection accepted: %s:%d" %(self.client_address[0], self.client_address[1]))
|
||||
|
||||
def handle(self):
|
||||
while True:
|
||||
@ -499,7 +527,7 @@ class kmsServerHandler(socketserver.BaseRequestHandler):
|
||||
|
||||
def finish(self):
|
||||
self.request.close()
|
||||
loggersrv.info("Connection closed: %s:%d" % (self.client_address[0], self.client_address[1]))
|
||||
loggersrv.info("Connection closed: %s:%d" %(self.client_address[0], self.client_address[1]))
|
||||
|
||||
|
||||
serverqueue = Queue.Queue(maxsize = 0)
|
||||
|
Loading…
Reference in New Issue
Block a user