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

Last change on this file since 6939 was 6939, checked in by mabarracus, 19 months ago

Fix local executable filter

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