#!/usr/bin/python3

import os
import glob
import stat
import shutil
import re
import subprocess

import ogGlobals
from InventoryLib import ogIsEfiActive
from DiskLib import ogGetEsp
from FileSystemLib import ogMount, ogUnmount

## sets envvar and writes to /etc/environment
def env_export (k, v):
    os.environ[k] = str (v)
    already_present = False
    with open ('/etc/environment', 'r') as env_fd:
        for line in env_fd:
            if re.search (f'^{k}=', line):
                already_present = True
                break
    if not already_present:
        with open ('/etc/environment', 'a') as env_fd:
            env_fd.write (f'{k}={v}\n')
    return v

def loadenviron():
    # Idioma por defecto.
    LANG = os.environ.get ('LANG', 'es_ES')
    env_export ('LANG', 'es_ES')

    # Directorios del proyecto OpenGnsys.
    OPENGNSYS = os.environ.get ('OPENGNSYS', '/opt/opengnsys')
    env_export ('OPENGNSYS',  OPENGNSYS)

    OGBIN     = env_export ('OGBIN',     f'{OPENGNSYS}/bin')
    OGETC     = env_export ('OGETC',     f'{OPENGNSYS}/etc')
    OGLIB     = env_export ('OGLIB',     f'{OPENGNSYS}/lib')
    OGAPI     = env_export ('OGAPI',     f'{OGLIB}/engine/bin')
    OGPYFUNCS = env_export ('OGPYFUNCS', f'{OPENGNSYS}/functions')
    OGSCRIPTS = env_export ('OGSCRIPTS', f'{OPENGNSYS}/scripts')
    OGIMG     = env_export ('OGIMG',     f'{OPENGNSYS}/images')
    OGCAC     = env_export ('OGCAC',     f'{OPENGNSYS}/cache')
    OGLOG     = env_export ('OGLOG',     f'{OPENGNSYS}/log')

    # Exportar parámetros del kernel.
    with open ('/proc/cmdline', 'r') as fd: cmdline = fd.read()
    kvars = cmdline.split()
    for kvar in kvars:
        if '=' not in kvar: continue
        k, v = kvar.split ('=')
        env_export (k, v)

    OGSERVERIMAGES = os.environ.get ('OGSERVERIMAGES', '')
    if 'ROOTREPO' not in os.environ or not os.environ.get ('ROOTREPO'):
        env_export ('ROOTREPO', OGSERVERIMAGES)

    # Compatibilidad para usar proxy en clientes ogLive.
    http_proxy = os.environ.get ('http_proxy', '')
    ogproxy    = os.environ.get ('ogproxy',    '')
    if not http_proxy and ogproxy:
        env_export ('http_proxy', ogproxy)

def write_profile():
    ## si estos accesos a os.environ fallan, es porque loadenviron() no se ejecutó
    OGSCRIPTS  = os.environ['OGSCRIPTS']
    OGPYFUNCS  = os.environ['OGPYFUNCS']
    OGBIN      = os.environ['OGBIN']
    PYTHONPATH = os.environ['PYTHONPATH']
    LANG       = os.environ['LANG']

    PATH       = os.environ.get ('PATH', '')

    L = LANG[0:LANG.index('_')]
    with open ('/etc/profile.d/ogprofile.sh', 'w') as fd:
        fd.write (f'''
(locale-gen {LANG} &>/dev/null &)
export PATH={OGSCRIPTS}:{OGPYFUNCS}:{OGBIN}:{PATH}:/opt/oglive/rootfs/opt/drbl/sbin
export PYTHONPATH={PYTHONPATH}
sysctl -p &>/dev/null
loadkeys {L} >/dev/null
if [ ! -f /run/resolvconf/resolv.conf -a -n "$ogdns" ]; then
    mkdir -p /run/resolvconf
    echo "nameserver $ogdns" > /run/resolvconf/resolv.conf
fi
        '''.strip() + '\n')

def clean_esp():
    ## BootLib.bootOs() makes a copy of grub.cfg in /boot/grub within the ESP
    ## clean it up
    if ogIsEfiActive():
        esp = ogGetEsp()
        if esp:
            esp_disk, esp_par = esp.split()
            mntpt = ogMount (esp_disk, esp_par)
            if os.path.exists (f'{mntpt}/boot/grub/grub.cfg'):
                os.unlink (f'{mntpt}/boot/grub/grub.cfg')
            ogUnmount (esp_disk, esp_par)

def fileslinks():
    print (ogGlobals.lang.MSG_MAKELINKS)

    # Shell BASH por defecto (para usar "runtest")
    if os.path.exists ('/bin/sh'): os.unlink ('/bin/sh')
    os.symlink ('/bin/bash', '/bin/sh')

    # Crear directorio de bloqueos
    ## for some reason we need try/catch even when using 'exist_ok=True'
    try: os.makedirs ('/var/lock', exist_ok=True)
    except FileExistsError: pass
    try: os.makedirs ('/run/lock', exist_ok=True)
    except FileExistsError: pass

    # Crear ficheros temporales.
    for f in [ogGlobals.OGLOGCOMMAND, ogGlobals.OGLOGCOMMAND + '.tmp', ogGlobals.OGLOGSESSION, '/tmp/menu.tmp']:
        open (f, 'w').close()
        os.chmod (f, 0o777)

        # Enlaces para Qt Embeded.
        # son cosas de QT4, ya no aplica

        # Autenticación con clave pública para SSH
        if os.path.exists ('/scripts/ssl/authorized_keys'):
            for f in glob.glob ('/scripts/ssl/*'):
                shutil.copy2 (f, '/root/.ssh/')

def loadmodules():
    print (ogGlobals.lang.MSG_LOADMODULES)

    # Módulo del ratón.
    subprocess.run (['modprobe', 'psmouse'], stderr=subprocess.DEVNULL)

    # Cargar módulos específicos del kernel del cliente.
    uname_r = os.uname()[2]
    for m in glob.glob (f'{ogGlobals.OGLIB}/modules/{uname_r}/*.ko'):
        subprocess.run (['insmod', m], capture_output=True)

def metadevs():
    print (ogGlobals.lang.MSG_DETECTLVMRAID)
    subprocess.run (['vgchange', '-ay'], capture_output=True)   # Detectar metadispositivos LVM.
    subprocess.run (['dmraid', '-ay'], capture_output=True)     # Detectar metadispositivos RAID.

def mountrepo():
    ## si estos accesos a os.environ fallan, es porque loadenviron() no se ejecutó
    ROOTREPO      = os.environ['ROOTREPO']

    ROOTSERVER    = os.environ.get ('ROOTSERVER', '')
    ogactiveadmin = os.environ.get ('ogactiveadmin', '')
    ogprotocol    = os.environ.get ('ogprotocol', '')
    ogstatus      = os.environ.get ('ogstatus', '')
    SERVER        = os.environ.get ('SERVER', '')
    if not ROOTREPO: ROOTREPO = ROOTSERVER

    # TODO Revisar proceso de arranque para no montar 2 veces el repositorio.
    if 'true' == ogactiveadmin:
        os.environ['boot'] = 'admin'   # ATENCIÓN: siempre en modo "admin".
        subprocess.run (['umount', ogGlobals.OGIMG], stderr=subprocess.DEVNULL)

        if 'nfs' == ogprotocol:
            subprocess.run (['mount.nfs', f'{ROOTREPO}:{ogGlobals.OGIMG}', ogGlobals.OGIMG, '-o', 'rw,nolock'])

        elif 'smb' == ogprotocol:
            PASS = subprocess.run ('grep "^[ \t]*\\(export \\)\\?OPTIONS=" /scripts/ogfunctions 2>&1 | sed "s/\\(.*\\)pass=\\(\\w*\\)\\(.*\\)/\\2/"', shell=True, capture_output=True, text=True).stdout.strip()
            if not PASS: PASS = 'og'
            subprocess.run (['mount.cifs', f'//{ROOTREPO}/ogimages', ogGlobals.OGIMG, '-o', f'rw,serverino,acl,username=opengnsys,password={PASS}'])

        elif 'local' == ogprotocol:  # TODO: hacer funcion dentro de este script que monte smb
            # Comprobamos que estatus sea online.
            if 'offline' == ogstatus or '' == SERVER:
                # Si estatus es offline buscamos un dispositivo con etiqueta repo
                # y si no existe montamos la cache como repo (si existe).
                TYPE = subprocess.run ('blkid | grep REPO | awk -F"TYPE=" \'{print $2}\' | tr -d \\"', shell=True, capture_output=True, text=True).stdout.strip()
                if '' == TYPE:
                    cac_img = f'{ogGlobals.OGCAC}/{ogGlobals.OGIMG}'
                    if os.path.isdir (cac_img):
                        subprocess.run (['mount', '--bind', cac_img, ogGlobals.OGIMG])
                else:
                    subprocess.run (['mount', '-t', TYPE, 'LABEL=REPO', ogGlobals.OGIMG])
            else:
                # Comprobamos que existe un servicio de samba.
                rc = subprocess.run (['smbclient', '-L', SERVER, '-N'], capture_output=True, text=True).returncode
                if not rc:      ## success
                    PASS = subprocess.run ('grep "^[ \t]*\\(export \\)\\?OPTIONS=" /scripts/ogfunctions 2>&1 | sed "s/\\(.*\\)pass=\\(\\w*\\)\\(.*\\)/\\2/"', shell=True, capture_output=True, text=True).stdout.strip()
                    if not PASS: PASS = 'og'
                    subprocess.run (['mount.cifs', f'//{ROOTREPO}/ogimages', ogGlobals.OGIMG, '-o', f'rw,serverino,acl,username=opengnsys,password={PASS}'])

def poweroff():
    ogntp  = os.environ.get ('ogntp', '')
    status = os.environ.get ('status', '')

    print (ogGlobals.lang.MSG_POWEROFFCONF)
    # Sincronización horaria con servidor NTP.
    if ogntp and 'offline' != status:
        subprocess.run (['ntpdate', ogntp])

    # Crear fichero de configuración por defecto (30 min. de espera).
    POWEROFFCONF = '/etc/poweroff.conf'
    with open (POWEROFFCONF, 'w') as fd:
        fd.write (f'''
POWEROFFSLEEP=30
POWEROFFTIME=
        '''.strip() + '\n')
    # Incluir zona horaria en el fichero de configuración.
    awk_out = subprocess.run (['awk', 'BEGIN {RS=" "} /^TZ=/ {print}', '/proc/cmdline'], capture_output=True, text=True).stdout.strip()
    with open (POWEROFFCONF, 'a') as fd:
        fd.write (awk_out + '\n')

    # Lanzar el proceso "cron".
    subprocess.run (['cron', '-l'])

    # Definir la "crontab" lanzando el proceso de comprobación cada minuto.
    crontab_entry = f'* * * * *   [ -x {ogGlobals.OGBIN}/poweroffconf ] && {ogGlobals.OGBIN}/poweroffconf\n'
    subprocess.run (['crontab', '-'], input=crontab_entry, text=True)

def filebeat():
    oglog = os.environ.get ('oglog', '')
    F = '/etc/filebeat/filebeat.yml'
    OPENSEARCH_PORT = '9200'

    if not os.path.exists (F): return

    PASS = subprocess.run ('grep "^[ \t]*\\(export \\)\\?OPTIONS=" /scripts/ogfunctions 2>&1 | sed "s/\\(.*\\)pass=\\(\\w*\\)\\(.*\\)/\\2/"', shell=True, capture_output=True, text=True).stdout.strip()     ## taken from a sibling script
    if not PASS: PASS = 'og'
    ogkdf = subprocess.run (f'echo -n {PASS} |sha256sum |cut -c1-12', shell=True, capture_output=True, text=True).stdout.strip()
    PASS = 'OG+' + ogkdf

    os.chmod (F, 0o600)
    subprocess.run (['sed', '-i',
        '-e', f's/__OGLOG_IP__/{oglog}/g',
        '-e', f's/__OGLOG_PORT__/{OPENSEARCH_PORT}/g',
        '-e', f's/__OPENSEARCH_PASSWORD__/{PASS}/g',
        F
    ])

    os.makedirs ('/var/log/filebeat', exist_ok=True)
    subprocess.Popen (['/usr/bin/filebeat', '-c', F, '--path.home', '/usr/share/filebeat', '--path.config', '/etc/filebeat', '--path.data', '/var/lib/filebeat', '--path.logs', '/var/log/filebeat'])

def stunnel():
    ogcore      = os.environ.get ('ogcore', '')
    ogusetls    = os.environ.get ('ogusetls', '')
    ogverifytls = os.environ.get ('ogverifytls', '')

    with open ('/etc/stunnel/menu.conf', 'w') as fd: fd.write (f'''
setuid = stunnel4
setgid = stunnel4
pid = /var/run/stunnel4/menu.pid
foreground = yes
debug = info

[menu]
client = yes
accept = 127.0.0.1:81
connect = {ogcore}:8443
    '''.strip() + '\n')
    if 'true' == ogusetls:
        if 'true' == ogverifytls:
            ## use tls and verify
            with open ('/etc/stunnel/menu.conf', 'a') as fd: fd.write (f'''
cert = /opt/opengnsys/etc/ogagent.crt
key = /opt/opengnsys/etc/ogagent.key
CAfile = /opt/opengnsys/etc/ca.crt
requireCert = yes
verifyChain = yes
            '''.strip() + '\n')
        else:
            ## use tls but not verify
            with open ('/etc/stunnel/menu.conf', 'a') as fd: fd.write (f'''
cert = /opt/opengnsys/etc/ogagent.crt
key = /opt/opengnsys/etc/ogagent.key
CAfile = /opt/opengnsys/etc/ca.crt
requireCert = no
verifyChain = no
            '''.strip() + '\n')
    else:
        ## don't use tls
        with open ('/etc/stunnel/menu.conf', 'a') as fd: fd.write (f'''
requireCert = no
verifyChain = no
    '''.strip() + '\n')

    os.makedirs ('/var/run/stunnel4', exist_ok=True)
    shutil.chown ('/var/run/stunnel4', 'stunnel4', 'stunnel4')
    with open ('/var/log/stunnel4/menu.log', 'a') as stunnel_log_fd:
        subprocess.Popen (['stunnel', '/etc/stunnel/menu.conf'], stdout=stunnel_log_fd, stderr=subprocess.STDOUT)

def dbus():
    if os.path.exists ('/etc/dbus-1/system.d/ogbrowser.conf'):
        os.makedirs ('/run/dbus', exist_ok=True)
        dbus_addr = subprocess.run (['dbus-daemon', '--print-address', '--system', '--nosyslog'], capture_output=True, text=True).stdout.strip()
        env_export ('DBUS_SESSION_BUS_ADDRESS', dbus_addr)
        with open ('/var/log/dbus-monitor.log', 'w') as dbus_mon_fd:
            subprocess.Popen (['dbus-monitor', '--system'], stdout=dbus_mon_fd, stderr=subprocess.STDOUT)

def otherservices():
    DEVICE      = os.environ.get ('DEVICE', '')

    if ogIsEfiActive():
        subprocess.run (['mount', '-t', 'efivarfs', 'none', '/sys/firmware/efi/efivars'])

    print (ogGlobals.lang.MSG_OTHERSERVICES)

    if os.path.exists ('/dev/log') and not stat.S_ISSOCK (os.stat ('/dev/log').st_mode): 
        subprocess.run (['service', 'rsyslog', 'start'])

    # root password
    PASS = subprocess.run ('grep "^[ \t]*\\(export \\)\\?OPTIONS=" /scripts/ogfunctions 2>&1 | sed "s/\\(.*\\)pass=\\(\\w*\\)\\(.*\\)/\\2/"', shell=True, capture_output=True, text=True).stdout.strip()
    if not PASS: PASS = 'og'
    subprocess.run (['passwd', 'root'], input=f'{PASS}\n{PASS}\n', stderr=subprocess.DEVNULL, text=True)

    subprocess.run (['/etc/init.d/ssh', 'start'], capture_output=True, text=True)

    #setterm -blank 0 -powersave off -powerdown 0 < /dev/console > /dev/console 2>&1     ## apagado de monitor
    subprocess.run (['ethtool', '-s', DEVICE, 'wol', 'g'], stderr=subprocess.DEVNULL) ## Activado WOL en la interfaz usada en arranque PXE.

def runhttplog():
    ## si estos accesos a os.environ fallan, es porque loadenviron() no se ejecutó
    OPENGNSYS    = os.environ['OPENGNSYS']

    try:
        shutil.copy2 ('/etc/lighttpd/lighttpd.conf',            '/etc/lighttpd/lighttpd.conf.back')
        shutil.copy2 ('/etc/lighttpd/conf-enabled/10-cgi.conf', '/etc/lighttpd/conf-enabled/10-cgi.conf.back')
    except FileNotFoundError:
        pass

    shutil.copy2 (f'{OPENGNSYS}/lib/httpd/lighttpd.conf', '/etc/lighttpd/')
    shutil.copy2 (f'{OPENGNSYS}/lib/httpd/10-cgi.conf',   '/etc/lighttpd/conf-enabled/')

    subprocess.run (['/etc/init.d/lighttpd', 'start'], capture_output=True)

    o = OPENGNSYS
    while '/' != o:
        os.chmod (o, 0o755)
        o = os.path.dirname (o)
    os.makedirs ('/usr/lib/cgi-bin', exist_ok=True)

    for filename in os.listdir (f'{OPENGNSYS}/lib/httpd/'):
        full_file_name = os.path.join (f'{OPENGNSYS}/lib/httpd/', filename)
        if os.path.isfile (full_file_name):
            shutil.copy2 (full_file_name, '/usr/lib/cgi-bin')

    ## dstat fails because /var/lib/pcp/pmns/root does not exist.
    ## we work around that by starting/stopping pmcd once. When it starts, it creates the missing file.
    if not os.path.exists ('/var/lib/pcp/pmns/root'):
        subprocess.run (['/etc/init.d/pmcd', 'start'], capture_output=True)
        subprocess.run (['/etc/init.d/pmcd', 'stop'], capture_output=True)

    with open ('/tmp/bandwidth', 'w') as f:
        subprocess.Popen (['dstat', '-dn', '10'], stdout=f)

    with open (ogGlobals.OGLOGSESSION, 'a') as fd:
        fd.write ('WAITING\n')
