source: lliurex-statistics/trunk/fuentes/lliurex-statistics.install/usr/sbin/analytics @ 6968

Last change on this file since 6968 was 6968, checked in by mabarracus, 2 years ago

Merge some changes from python2 implementation

  • Property svn:executable set to *
File size: 27.2 KB
Line 
1#!/usr/bin/env python3
2import sys
3import os
4import re
5import signal
6import time
7import subprocess
8import requests
9import json
10import configparser
11import daemon
12from xmlrpc import client
13import lockfile
14import logging.handlers
15from logging import config as cfg
16import ssl
17import psutil
18
19
20oldsignals = {}
21for sig in signal.Signals:
22    try:
23        oldsignals.setdefault(sig.name, signal.getsignal(sig))
24        signal.signal(sig.value, signal.SIG_IGN)
25    except:
26        continue
27
28#
29# START EDITABLE VARS (OVERRIDES VALUES IN CONFIGFILE)
30#
31
32# DEBUG = 1
33
34MODE = 'PROCEDURAL'
35# MODE = 'THREADED'
36
37CONFIGFILE = '/etc/lliurex-analytics/agent.cfg'
38# CONFIGFILE = 'config.txt'
39
40# OVERRIDE_SEND_PERMISSION   = 1
41
42# MIN_LOG_LEVEL = 'info'
43
44# DAEMON_MODE = 1
45
46# FILELOCK = '/tmp/analytics'
47
48PIDFILE = '/var/run/analytics.pid'
49FILELOCK = '/var/run/analytics.pid'
50
51# PIDFILE = '/tmp/analitics.pid'
52
53# STATUSFILE = '/etc/lliurex-analytics/status'
54
55# TIMEOUT = 1
56
57#
58# END EDITABLE VARS #
59#
60
61
62if MODE == 'PROCEDURAL':
63    from multiprocessing import Process, Manager, get_logger
64    str_formatter = '(%(processName)s)'
65if MODE == 'THREADED':
66    from multiprocessing.dummy import Process, Manager
67        from multiprocessing import get_logger
68    str_formatter = '(%(threadName)s)'
69
70def get_var_value(varname, config=None, mode='string', section='Agent'):
71    value = None
72
73    if config:
74        varname = varname.lower()
75        try:
76            if mode == 'string':
77                value = config.get(section, varname)
78            elif mode == 'bool':
79                value = config.getboolean(section, varname)
80            elif mode == 'int':
81                value = config.getint(section, varname)
82            elif mode == 'float':
83                value = config.getfloat(section, varname)
84        except:
85            pass
86
87    def f(item):
88        if isinstance(item, str) or isinstance(item, bool) or isinstance(item, int) or isinstance(item, float):
89            return True
90        else:
91            return False
92
93    for x in (v for v in globals() if varname.lower() == v.lower() and f(globals()[v])):
94        value = globals()[x]
95
96    if mode == 'string':
97        return str(value)
98    elif mode == 'bool':
99        return bool(value)
100    elif mode == 'int':
101        return int(value)
102    elif mode == 'float':
103        return float(value)
104    else:
105        return value
106
107
108if get_var_value('DEBUG', mode='bool'):
109    loglevel = get_var_value('MIN_LOG_LEVEL')
110    if loglevel:
111        if loglevel == 'debug' or loglevel == logging.DEBUG:
112            loglevel = logging.DEBUG
113        elif loglevel == 'critical' or loglevel == logging.CRITICAL:
114            loglevel = logging.CRITICAL
115        elif loglevel == 'error' or loglevel == logging.ERROR:
116            loglevel = logging.ERROR
117        elif loglevel == 'warning' or loglevel == logging.WARNING:
118            loglevel = logging.WARNING
119        elif loglevel == 'info' or loglevel == logging.INFO:
120            loglevel = logging.INFO
121        else:
122            loglevel = logging.DEBUG
123    else:
124        loglevel = logging.DEBUG
125else:
126    loglevel = logging.INFO
127
128
129LOGGING = {
130    'version': 1,
131    'disable_existing_loggers': False,
132    'formatters': {
133        'verbose': {
134#            'format': '%(levelname)s %(module)s (%(pathname)s:%(lineno)d) ' + str_formatter  + ' %(message)s'
135            'format': '%(levelname)s %(module)s ' + str_formatter + ' %(message)s'
136        },
137    },
138    'handlers': {
139        'stdout': {
140            'class': 'logging.StreamHandler',
141            'stream': sys.stdout,
142            'formatter': 'verbose',
143        },
144        'sys-logger6': {
145            'class': 'logging.handlers.SysLogHandler',
146            'address': '/dev/log',
147            'facility': "local6",
148            'formatter': 'verbose',
149        },
150    },
151    'loggers': {
152        'analytics-logger': {
153            'handlers': ['sys-logger6', 'stdout'],
154            'level': loglevel,
155            'propagate': True,
156        },
157    }
158}
159
160
161cfg.dictConfig(LOGGING)
162log = logging.getLogger('analytics-logger')
163
164
165def print_config(config):
166    global log
167
168    for sect in config:
169        for key in config[sect]:
170            log.debug("Config Loaded: '{}' '{}' '{}'".format(sect, key, config[sect][key]))
171
172
173def init_config():
174
175    config = configparser.ConfigParser()
176    config.read(CONFIGFILE)
177
178    return config
179
180
181def init_logging(config):
182    global DEBUG, log, loglevel
183
184    mpl = get_logger()
185    mpl.setLevel(loglevel)
186    for hdl in log.handlers:
187        mpl.addHandler(hdl)
188    if get_var_value('DEBUG', config):
189        DEBUG = True
190        log.setLevel(loglevel)
191        print_config(config)
192
193
194def bin_to_ascii(value):
195    global log
196
197    try:
198        if isinstance(value, bytes):
199            return value.decode('utf-8')
200        else:
201            return value
202    except Exception as e:
203        log.error('Error bin_to_ascii {}'.format(e))
204        return value
205
206
207def get_llx_version():
208    global log
209
210    output = bin_to_ascii(subprocess.check_output(['lliurex-version','-n']))
211    release = output[0:2].strip()
212    if release == '15':
213        use = ['lliurex-detect','-f']
214        output = bin_to_ascii(subprocess.check_output(use))
215    else:
216        use = ['lliurex-version','-f']
217        output = bin_to_ascii(subprocess.check_output(use))
218    flavour = output.strip()
219    #log.info("Detected release:'{}' flavour:'{}'".format(release, flavour))
220    return release, flavour
221
222
223def detect_proxy():
224    global log
225
226    px = subprocess.Popen(["bash", "-c", "source /etc/profile && echo $http_proxy"], stdout=subprocess.PIPE)
227    proxy = bin_to_ascii(px.stdout.readline()).strip()
228    #log.info("Detected proxy: '{}'".format(proxy))
229    return proxy
230
231
232def daemonize(*args, **kwargs):
233    global glob, log, CONFIG
234
235    log.info('Running daemon mode...')
236    filelock = get_var_value('filelock', CONFIG)
237    if not filelock:
238        filelock = '/var/run/analytics.pid'
239
240    try:
241        with daemon.DaemonContext(detach_process=True,
242                                  working_directory='/tmp',
243                                  umask=0o002,
244                                  pidfile=lockfile.FileLock(filelock),
245                                  files_preserve=[log.handlers[0].socket.fileno()]):
246            start(**kwargs)
247    except Exception as e:
248        log.critical("Error daemonizing {}".format(e))
249        sys.exit(1)
250
251
252def add_item(item,regexp):
253    global glob, log
254
255    log.debug('Request to add {}'.format(item))
256
257    parts_item = item.split(' ')
258
259    executable = None
260    for i in range(len(parts_item)):
261        executable = parts_item[i].strip()
262        if '/' in executable:
263            executable = executable.split('/')[-1]
264            log.debug('Trimming executable to {}'.format(executable))
265        if not re.match(regexp,executable):
266            log.debug('Skipping malformed executable {}'.format(executable))
267            executable = None
268            continue
269        if executable in glob['INTERPRETERS']:
270            log.debug('Trimming interpreter part {}'.format(executable))
271            executable = None
272            continue
273        else:
274            if executable in glob['BLACKLIST']:
275                log.debug('Skipping add due to blacklisted command {}'.format(executable))
276                return None
277            log.debug('Valid executable {}'.format(executable))
278            break
279
280    the_list = glob['LIST']
281    if executable:
282        if executable in the_list:
283            the_list[executable] = the_list[executable] + 1
284            log.debug('+++ Incrementing {} = {}'.format(executable, the_list[executable]))
285        else:
286            log.debug('*** Adding {} = 1'.format(executable, 1))
287            the_list[executable] = 1
288    glob['LIST'] = the_list
289
290def monitor():
291    global glob, log
292
293    log.info('Start monitor')
294    logfilename = get_var_value('file', glob['config'], section='Audit')
295
296    glob['BLACKLIST'] = get_var_value('blacklist', glob['config'], section='Audit')
297    glob['INTERPRETERS'] = get_var_value('interpreters', glob['config'], section='Audit')
298
299    try:
300        glob['INTERPRETERS'] = [x.strip() for x in glob['INTERPRETERS'].split(',')]
301    except Exception as e:
302        log.error('Malformed interpreters list ,{}'.format(e))
303        glob['INTERPRETERS'] = []
304        return None
305
306    try:
307        with open(glob['BLACKLIST'], 'r') as fp:
308            glob['BLACKLIST'] = [line.strip() for line in fp]
309    except Exception as e:
310        log.error('Unable to read blacklist from {} , {}'.format(glob['BLACKLIST'], e))
311        glob['BLACKLIST'] = []
312        return None
313
314    try:
315        if not (os.path.isfile(logfilename) and os.access(logfilename, os.R_OK)):
316            log.critical('File {} not readable'.format(logfilename))
317            glob['TERMINATE'] = True
318
319        fp = subprocess.Popen(['tail', '-F', logfilename], stdout=subprocess.PIPE, stderr=open(os.devnull, 'w'))
320        glob['monitor_pid'] = fp.pid
321    except Exception as e:
322        log.critical('Error initializing {} read, {}'.format(logfilename, e))
323        glob['TERMINATE'] = True
324        return None
325
326    try:
327        log.info('Starting monitoring {}'.format(logfilename))
328        regexp = re.compile('^[a-zA-Z][a-zA-Z0-9_.+\-]+$')
329        while not glob['TERMINATE']:
330            if fp.poll() is not None:
331                log.error('Dead subprocess monitoring {}'.format(logfilename))
332                fp = subprocess.Popen(['tail', '-F', logfilename], stdout=subprocess.PIPE, stderr=open(os.devnull, 'w'))
333                glob['monitor_pid'] = fp.pid
334            else:
335                line = bin_to_ascii(fp.stdout.readline()).strip()
336                if re.search('type=EXECVE', line):
337                    m = re.findall('a[0-9]+="([^"]+)"', line)
338                    if m:
339                        captured = ' '.join(m)
340                        add_item(captured,regexp)
341
342    except Exception as e:
343        if isinstance(e, ConnectionResetError):
344            log.info('Connection reset exitting monitor thread')
345            glob['TERMINATE'] = True
346            return
347        else:
348            log.error('Error reading file {}, {}'.format(logfilename, e))
349            glob['TERMINATE'] = True
350            return
351
352    log.info('Exitting monitor thread')
353    return
354
355def update_list():
356    global glob, log
357
358    log.info('Start update list')
359    try:
360        list_path = get_var_value('list_path', glob['config'], mode='string', section='Server')
361        server = get_var_value('server', glob['config'], mode='string', section='Server')
362        url = 'http://' + server + '/' + list_path
363        agent = glob['user_agent']
364        headers = {'user-agent': agent}
365    except Exception as e:
366        log.warning('Error gettting update list settings {}'.format(e))
367
368    log.debug('List path {}'.format(url))
369
370    tick = 1
371    timeout = 60 * 60 * 12
372    c = 10
373
374    while not glob['TERMINATE']:
375        time.sleep(tick)
376        if c > 0:
377            c = c - tick
378        else:
379            c = timeout
380
381            sent = False
382            rq = None
383
384            try:
385                if glob['use_proxy']:
386                    proxy_obj = dict()
387                    proxy_obj.setdefault('http', glob['proxy'])
388
389                    rq = requests.get(url, headers=headers, proxies=proxy_obj, timeout=5)
390                    sent = True
391                else:
392                    rq = requests.get(url, headers=headers, timeout=5)
393                    sent = True
394            except Exception as e:
395                log.warning('Error getting list from {}, {}'.format(url,e))
396
397            try:
398                blist = glob['BLACKLIST']
399            except Exception as e:
400                log.error('Error loading current blacklist on update_list, {}'.format(e))
401
402            try:
403                the_list = glob['LIST']
404            except Exception as e:
405                log.error('Error loading current applist on update_list, {}'.format(e))
406
407            if sent and rq:
408                result = rq.text
409                try:
410                    json_list = json.loads(result)
411                except Exception as e:
412                    log.warning('Wrong list received {}, {}'.format(result,e))
413                    continue
414
415                try:
416                    for item in json_list:
417                        if item not in blist:
418                            blist.append(item)
419                            log.info("Received item list '{}'".format(item))
420                        if item in the_list:
421                            del the_list[item]
422                            log.info("Removed item from list '{}'".format(item))
423                    glob['BLACKLIST'] = blist
424                    glob['LIST'] = the_list
425                except Exception as e:
426                    log.error('Error updating blacklist, {}'.format(e))
427            else:
428                log.warning('Unable to get list data')
429
430    log.info('Exitting update list thread')
431
432
433def timed_send():
434    global glob, log
435
436    log.debug('Start timed_send ')
437    try:
438        count = get_var_value('timeout', glob['config'], mode='int')
439        if count < 0:
440            log.warning('Not valid timeout value setting default 300')
441    except Exception as e:
442        log.warning('Unable to read timeout value defaulting to 300, {}'.format(e))
443        count = 300
444
445    log.info('Initialized timed send with value {} seconds'.format(count))
446    c = count
447    tick = 0.2
448    try:
449        while not glob['TERMINATE']:
450            while glob['PRINTING'] == True:
451                time.sleep(1)
452            time.sleep(tick)
453            if c > 0:
454                c = c - tick
455            else:
456                c = count
457                log.debug('Triggering timed send')
458                clean_and_send()
459
460    except Exception as e:
461        if isinstance(e, ConnectionResetError):
462            log.info('Connection reset exitting timer thread')
463        else:
464            log.error('Error with timed send, {}'.format(e))
465
466    log.info('Exitting timer thread')
467    return
468
469
470def start(*args, daemon_mode=False,release='Unknown',flavour='Unknown',proxy=False, **kwargs):
471    global THREADS, oldsignals, log, CONFIG, glob
472
473    log.info("Starting analytics")
474    log.info('Initialization with release={} flavour={} proxy={}'.format(release,flavour,proxy))
475
476    mgr = Manager()
477    glob = mgr.dict()
478
479    glob['DAEMON_MODE'] = daemon_mode
480    glob['config'] = CONFIG
481
482    glob['release'] = release
483    glob['flavour'] = flavour
484    if proxy:
485        glob['proxy'] = proxy
486        glob['use_proxy'] = True
487    else:
488        glob['proxy'] = False
489        glob['use_proxy'] = False
490
491    pidfile = get_var_value('pidfile', glob['config'])
492
493    try:
494        server = get_var_value('server', glob['config'], section='Server')
495        server_path = get_var_value('server-path', glob['config'], section='Server')
496        if server.strip() == '' or server_path.strip() == '':
497            raise Exception('Empty server or server-path')
498        glob['server'] = server
499        glob['server_path'] = server_path
500    except Exception as e:
501        log.critical('Error getting server url, {}'.format(e))
502
503    try:
504        agent = get_var_value('user-agent', glob['config'])
505        if agent.strip() == '' or agent == 'None':
506            agent = 'lliurex-analytics-agent'
507        glob['user_agent'] = agent
508    except Exception as e:
509        log.warning('Error getting user-agent, {}'.format(e))
510
511    # write pid
512    try:
513        with open(pidfile, 'w') as fp:
514            fp.write(str(os.getpid()))
515    except Exception as e:
516        log.error('Error writting pidfile {}'.format(e))
517
518    glob['TERMINATE'] = False
519    glob['PRINTING'] = False
520    glob['LIST'] = {}
521
522    glob['platform_data'] = get_platform_data()
523
524    THREADS = dict()
525    THREADS['monitor'] = Process(target=monitor, name='monitor')
526    THREADS['monitor'].daemon = glob['DAEMON_MODE']
527
528    THREADS['timed_send'] = Process(target=timed_send, name='timed_send')
529    THREADS['timed_send'].daemon = glob['DAEMON_MODE']
530
531    THREADS['update_list'] = Process(target=update_list, name='update_list')
532    THREADS['update_list'].daemon = glob['DAEMON_MODE']
533
534    THREADS['monitor'].start()
535    THREADS['timed_send'].start()
536    THREADS['update_list'].start()
537
538    signals = {'SIGTERM': interrupt, 'SIGINT': interrupt, 'SIGUSR1': clean_and_send, 'SIGUSR2': show_captured}
539    for sig in oldsignals:
540        if sig in signals:
541            signal.signal(signal.__dict__[sig], signals[sig])
542        else:
543            try:
544                signal.signal(signal.__dict__[sig], oldsignals[sig])
545            except:
546                continue
547
548
549def clean_and_send(*args, **kwargs):
550    global glob, log
551
552    override_send_permission = get_var_value('override_send_permission', glob['config'], mode='bool')
553
554    if allow_send() or override_send_permission:
555        send_data(glob['LIST'])
556    else:
557        log.info('Sending not allowed when try to send results')
558    glob['LIST'] = {}
559
560
561def get_mac():
562    global log
563
564    dirmac = '/sys/class/net'
565    eth = 'eth0'
566    filemac = 'address'
567    file = '{}/{}/{}'.format(dirmac, eth, filemac)
568    uid = None
569    try:
570        with open(file, 'r') as fp:
571            uid = bin_to_ascii(fp.read()).strip()
572    except:
573        log.warning('Unable to read {}'.format(file))
574        eth = sorted(os.listdir(dirmac))
575        if len(eth) > 0:
576            eth = eth[0]
577        file = '{}/{}/{}'.format(dirmac, eth, filemac)
578        try:
579            with open(file, 'r') as fp:
580                uid = bin_to_ascii(fp.read()).strip()
581        except Exception as e:
582            log.error('Unable to read mac address, {}'.format(e))
583
584    return str(uid)
585
586
587def get_cpu():
588    global log
589
590    file = '/proc/cpuinfo'
591    cpu = {}
592    try:
593        with open(file, 'r') as fp:
594            for line in fp:
595                if re.search('^processor\s+:\s+([0-9]+)$', line):
596                    m = re.findall('^processor\s+:\s+([0-9]+)', line)
597                    if m and len(m) > 0:
598                        cpu['ncpus'] = int(m[0]) + 1
599                if re.search('^model name\s+:\s+(.+)$', line):
600                    m = re.findall('^model name\s+:\s+(.+)$', line)
601                    if m and len(m) > 0:
602                        cpu['model'] = str(m[0])
603    except Exception as e:
604        log.warning('Unable to read cpuinfo, {}'.format(e))
605        cpu = None
606    return cpu
607
608
609def get_mem():
610    global log
611
612    file = '/proc/meminfo'
613    mem = None
614
615    try:
616        with open(file, 'r') as fp:
617            for line in fp:
618                if re.search('^MemTotal:\s+([0-9]+)\s+\S+$', line):
619                    m = re.findall('^MemTotal:\s+([0-9]+)\s+\S+$', line)
620                    if m and len(m) > 0:
621                        mem = int(m[0])
622                        break
623    except Exception as e:
624        log.warning('Unable to read meminfo, {}'.format(e))
625        mem = None
626    return str(mem)
627
628
629def get_vga():
630    global log
631
632    vga = None
633    try:
634        out = bin_to_ascii(subprocess.check_output(['lspci'])).split('\n')
635        for line in out:
636            line_strip = line.strip()
637            if re.search('VGA', line_strip, re.IGNORECASE):
638                m = re.findall('^\S+\s(.+)$', line_strip)
639                if m and len(m) > 0:
640                    vga = m[0]
641                    break
642    except Exception as e:
643        log.warning('Unable to read pciinfo, {}'.format(e))
644        vga = None
645    return str(vga)
646
647
648def get_arch():
649    global log
650
651    arch = None
652    try:
653        arch = bin_to_ascii(subprocess.check_output(['uname', '-m'])).strip()
654    except Exception as e:
655        log.warning('Unable to read architecture, {}'.format(e))
656        arch = None
657    return str(arch)
658
659
660def get_platform_data():
661    global log
662
663    data = {}
664    data.setdefault('mac', get_mac())
665    data.setdefault('cpu', get_cpu())
666    data.setdefault('mem', get_mem())
667    data.setdefault('vga', get_vga())
668    data.setdefault('arch', get_arch())
669
670    log.debug("Detected mac='{}' arch='{}' cpu='{}' mem='{}' vga='{}'".format(data['mac'], data['arch'], data['cpu'], data['mem'], data['vga']))
671    return data
672
673
674def send_data(data):
675    global log, glob
676
677    log.debug('sending specs {}'.format(glob['platform_data']))
678    log.debug('sending data {}'.format(glob['LIST']))
679
680    agent = glob['user_agent']
681    url = 'http://' + glob['server'] + '/' + glob['server_path']
682    headers = {'user-agent': agent}
683
684    version = glob['release']
685    flavour = glob['flavour']
686
687    list_data = data
688    try:
689        json_list_data = json.dumps(list_data)
690    except Exception as e:
691        log.error('Json error on internal data list')
692        return None
693
694    platform_data = glob['platform_data']
695    uid = platform_data['mac']
696
697    data_to_send = dict()
698    data_to_send.setdefault('uid', uid)
699    data_to_send.setdefault('vers', version)
700    data_to_send.setdefault('sab', flavour)
701    data_to_send.setdefault('specs', platform_data)
702    data_to_send.setdefault('stats', json_list_data)
703
704    try:
705        json_data_to_send = json.dumps(data_to_send)
706    except Exception as e:
707        log.error('Json error on data to send')
708        return None
709
710    payload = {'stats': json_data_to_send}
711    log.debug('Payload to send: {}'.format(payload))
712
713    sent = False
714    rq = None
715    if glob['use_proxy']:
716        proxy_obj = dict()
717        proxy_obj.setdefault('http', glob['proxy'])
718        try:
719            rq = requests.post(url, data=payload, headers=headers, proxies=proxy_obj, timeout=5)
720            sent = True
721        except Exception as e:
722            log.error('Error sending data through proxy, {}'.format(e))
723
724    if not glob['use_proxy'] or sent == False:
725        try:
726            rq = requests.post(url, data=payload, headers=headers, timeout=5)
727            sent = True
728        except Exception as e:
729            log.error('Error sending data, {}'.format(e))
730
731    if sent and rq:
732        result = rq.text
733        result = result.strip().lower()
734        if result == 'ok':
735            log.debug('Sending was success with reply OK ')
736        elif result == 'nok':
737            log.info('Sending was success but reply is NOK ')
738        else:
739            log.warning("Sending was success but reply is unknown '{}'".format(result))
740    else:
741        log.warning('Unable to send data')
742
743
744def interrupt(*args, **kwargs):
745    global glob, log, THREADS, mgr
746
747    log.info('Interrupting analytics')
748   
749    try:
750        clean_and_send()
751        try:
752            glob['TERMINATE'] = True
753            os.kill(glob['monitor_pid'],signal.SIGKILL)
754        except Exception as e:
755            log.error('Requested kill the program {}'.format(e))
756            sys.exit(1)
757        for x in THREADS:
758            THREADS[x].join()
759            log.info('Interrupt: Joined {} {}'.format(x))
760
761    except Exception as e:
762        log.error('Error while interrupting, {}'.format(e))
763
764
765def show_captured(*args, **kwargs):
766    global glob, log
767
768    glob['PRINTING'] = True
769    log.info('Requested to show list')
770
771    list_items = glob['LIST']
772    if not isinstance(list_items, dict):
773        log.warning('Error showing captured items, LIST is not a dictionary')
774
775    listkeys_sorted = sorted(list_items, key=list_items.get, reverse=True)
776
777    if len(listkeys_sorted) > 0:
778        log.info('analytics is showing currently capture list in memory')
779        for i in listkeys_sorted:
780            log.info('{} = {}'.format(i, list_items.get(i)))
781    else:
782        log.info('analytics detect an empty capture list in memory')
783
784    glob['PRINTING'] = False
785
786
787def check_server_acknowledge():
788    global log
789
790    try:
791        c = client.ServerProxy("https://server:9779/",
792                               verbose=False,
793                               use_datetime=True,
794                               context=ssl._create_unverified_context())
795        return c.get_variable("", "VariablesManager", "STATS_ENABLED")
796    except Exception as e:
797        log.error('Error getting variables, {}'.format(e))
798        return None
799
800
801def check_local_acknowledge():
802    global glob, log
803
804    if glob['TERMINATE']:
805        return None
806
807    try:
808        statusfile = get_var_value('statusfile', glob['config'])
809        if str(statusfile) == 'None':
810            statusfile = '/etc/lliurex-analytics/status'
811            log.warning('Warning statusfile not set, defaulting to {}'.format(statusfile))
812    except Exception as e:
813        log.error('Error getting value for statusfile, {}'.format(e))
814
815    answer = None
816    try:
817
818        if os.path.isfile(statusfile):
819            fp = open(statusfile, 'r')
820            answer = fp.readline()
821            fp.close()
822        else:
823            log.error('wrong statusfile {}'.format(statusfile))
824            return None
825
826        return answer.strip()
827    except Exception as e:
828        log.warning('Error reading status file, {}'.format(e))
829        return None
830
831
832def allow_send():
833    global glob, log
834
835    if glob['TERMINATE']:
836        return False
837
838    if glob['flavour'].lower() == 'server':
839        answer = str(check_server_acknowledge())
840        answer = answer.strip()
841        if answer == '1':
842            log.info('Allowed to send stats checking server acknowledge')
843            return True
844        elif answer == '0':
845            log.info('Denied to send stats checking server acknowledge')
846            return False
847        elif answer == 'None':
848            pass
849        else:
850            log.info('Unknown value checking server acknowledge, {}'.format(answer))
851    answer = str(check_local_acknowledge()).lower()
852    answer = answer.strip()
853    if answer == 'yes':
854        log.info('Allowed to send stats checking local acknowledge')
855        return True
856    elif answer == 'no':
857        log.info('Denied to send stats checking local acknowledge')
858        return False
859    elif answer == '':
860        pass
861    else:
862        log.info('Unknown value checking local acknowledge, {}'.format(answer))
863
864    log.info('Denied to send stats by default')
865    return False
866
867
868if __name__ == '__main__':
869    exit = 0
870    keyword='analytics'
871    interpreter='python3'
872    for proc in psutil.process_iter():
873        a=False
874        b=False
875        for argument in proc.cmdline():
876            #print('{} {} {}'.format(proc.cmdline(),keyword,argument[-len(keyword):]))
877            if interpreter in argument[-len(interpreter):]:
878                a = True
879            if keyword in argument[-len(keyword):]:
880                b = True
881            if a and b:
882                exit = exit +1
883    if exit > 1:
884        log.error('Another daemon is running')
885        sys.exit(1)
886
887    try:
888        CONFIG = init_config()
889    except Exception as e:
890        log.error('Error initializing config analytics {}'.format(e))
891        sys.exit(1)
892    try:
893        init_logging(CONFIG)
894    except Exception as e:
895        log.error('Error initializing logging analytics {}'.format(e))
896        sys.exit(1)
897
898    THREADS = {}
899
900    try:
901        release, flavour = get_llx_version()
902    except Exception as e:
903        log.error('Error getting llx version {}'.format(e))
904        release = 'Unknown'
905        flavour = 'Unknown'
906
907    proxy = ''
908    try:
909        proxy = detect_proxy()
910        if proxy == '':
911            #log.info('Not using proxy')
912            proxy = False
913    except Exception as e:
914        log.warning('Error detecting proxy {}'.format(e))
915        proxy = False
916
917    DAEMON_MODE = get_var_value('DAEMON_MODE', CONFIG, 'bool')
918
919    if DAEMON_MODE:
920        daemonize(daemon_mode=True,flavour=flavour,release=release,proxy=proxy)
921    else:
922        start(daemon_mode=False,flavour=flavour,release=release,proxy=proxy)
923
924    log.debug('End main')
925    for t in THREADS:
926        THREADS[t].join()
927        log.info('Joined {}'.format(t))
928    try:
929        mgr.shutdown()
930    except:
931        pass
932
933    log.info('Exitting analytics')
934    sys.exit(0)
Note: See TracBrowser for help on using the repository browser.