#!/usr/bin/python
# Mavericks AIMA upgrade script

import logging
import os
import sys
import syslog
import time
from isi.app.lib import safelockfile
from isi.ui.lib import nsswitch
import isi.fs.pctl2 as pctl2
import isi.sys.version as sysver

IFS_BACKUP_PATH = "/ifs/.ifsvar/tmp"
LOG_FILE = os.path.join(IFS_BACKUP_PATH, "mavericks-auth-upgrade.log")
AUTH_UPGRADE_FILE = os.path.join(IFS_BACKUP_PATH, "mavericks-auth-upgraded")

logger = logging.getLogger("mavericks_auth_upgrade")

def upgrade_nsswitch():
    logger.info("%s: Upgrading /etc/nsswitch.conf" % time.ctime())
    # NSSwitch
    nss = nsswitch.NSSwitch()

    group = nss.databases['group']
    nss.databases['group'] = []
    nss.databases['group'].append('lsass [success=return notfound=return tryagain=return unavail=continue]')
    if 'files' in group:
        nss.databases['group'].append('files')
    passwd = nss.databases['passwd']
    nss.databases['passwd'] = []
    nss.databases['passwd'].append('lsass [success=return notfound=return tryagain=return unavail=continue]')
    if 'files' in passwd:
        nss.databases['passwd'].append('files')
    netgroup = nss.databases['netgroup']
    nss.databases['netgroup'] = []
    nss.databases['netgroup'].append('lsass [success=return notfound=return tryagain=return unavail=continue]')
    if 'files' in netgroup:
        nss.databases['netgroup'].append('files')
    nss.save()

def upgrade_sam_path():
    """
    Move the local database file to a new directory.

    Turn the coalescer on to improve performance.  SQLite guarantees
    its own atomicity.

    Invariants:
    * lsassd is not running
    * new auth sam dir has been created
    * only one node executes this at a time
    """
    OLD_AUTH_SAM_PATH = "/ifs/.ifsvar/modules/auth/sam.db"
    NEW_AUTH_SAM_PATH = "/ifs/.ifsvar/modules/auth/sam/sam.db"

    if not os.path.exists(OLD_AUTH_SAM_PATH):
        return
    
    if not os.path.exists(NEW_AUTH_SAM_PATH):
        logger.info("%s: Upgrading sam path" % time.ctime())
        os.rename(OLD_AUTH_SAM_PATH, NEW_AUTH_SAM_PATH)

        try:
            # match "isi set -c on -F <path>"
            attr = pctl2.Expattr()
            attr.coalescing = True
            attr.endurant = False
            with open(NEW_AUTH_SAM_PATH, "rwb") as f:
                pctl2.set(f.fileno(), attr)
        except Exception, e:
            # log "isi set" error and continue
            syslog.syslog(syslog.LOG_NOTICE,
                          "Error enabling coalescer on local user database "
                          "%s (errno: %d)" % (NEW_AUTH_SAM_PATH, e.errno))

def upgrade_vsftpd_conf():
    """
    Add session_support=YES to vsftpd.conf if it does not exist already
    """
    vsftpd_path = "/etc/mcp/templates/vsftpd.conf"
    vsftpd_tmp_path = "/etc/mcp/templates/vsftpd.conf.tmp"
    session_support_seen = False
    chdir_homedir_seen = False
    
    if not os.path.exists(vsftpd_path):
        return

    with open(vsftpd_path, "r") as f:
        for line in f:
            if line.startswith("session_support="):
                session_support_seen = True
            if line.startswith("always_chdir_homedir="):
                chdir_homedir_seen = True

    if session_support_seen and chdir_homedir_seen:
        return

    f = open(vsftpd_path, "r")
    g = open(vsftpd_tmp_path, "w")

    for line in f:
        if (not session_support_seen) and line.startswith("#session_support="):
            g.write("session_support=$$session_support$$\n")
            session_support_seen = True
        elif (not chdir_homedir_seen) and line.startswith("#always_chdir_homedir="):
            g.write("always_chdir_homedir=$$always_chdir_homedir$$\n")
            chdir_homedir_seen = True
        else:
            g.write(line)
    f.close()
    if not session_support_seen:
        g.write("session_support=$$session_support$$\n")
    if not chdir_homedir_seen:
        g.write("always_chdir_homedir=$$always_chdir_homedir$$\n")
    g.close()

    diffStatus = os.system('/usr/bin/diff -B -u %s %s' %
                           (vsftpd_path, vsftpd_tmp_path))

    if os.WIFEXITED(diffStatus) and os.WEXITSTATUS(diffStatus) == 1:
        os.rename(vsftpd_tmp_path, vsftpd_path)
    else:
        os.remove(vsftpd_tmp_path)

    return

    
def main():
    nstat = sysver.Node_Status()
    prevNodeVersion = nstat.get_previous_version()
    if prevNodeVersion is None:
        return 0

    need_upgrade = False
    premav_upgrade = nstat.upgraded_across_mavericks()
    if premav_upgrade:
        need_upgrade = True
    else:
        prevNodeVersion = prevNodeVersion & 0xffffff0000000000
        # Also upgrade vsftpd on all 7.0 versions
        # and 7.0.1 versions up through 7.0.1.2
        if prevNodeVersion == sysver.MAVERICKS_VERSION:
            need_upgrade = True
        if prevNodeVersion == sysver.MAVERICKS_701_VERSION:
            dot_version = prevNodeVersion & 0xff00000
            if dot_version < 0x200000:
                need_upgrade = True

    if need_upgrade:
        try:
            logger.addHandler(logging.FileHandler(LOG_FILE, 'a'))
        except EnvironmentError, e:
            syslog.syslog(syslog.LOG_NOTICE,
                          "Error during logging "
                          "in mavericks-auth-upgrade script, %s" % e)
            return 0
        logger.setLevel(logging.DEBUG)

        try:
            os.chmod(LOG_FILE, 0640)
        except OSError, e:
            # log chmod errors and continue
            syslog.syslog(syslog.LOG_NOTICE,
                          "Error chmod'ing %s (errno: %d)" % (LOG_FILE, e.errno))

        # We need to block all other nodes from continuing until we've finished
        # the upgrade
        try:
            sentry = safelockfile(AUTH_UPGRADE_FILE, 'w')
        except EnvironmentError, e:
            syslog.syslog(syslog.LOG_NOTICE,
                          "Error during locking "
                          "in mavericks-auth-upgrade script, %s" % e)
            return 0
        if premav_upgrade:
            upgrade_nsswitch()
            upgrade_sam_path()
        upgrade_vsftpd_conf()
        sentry.close()

    return 0

if __name__ == "__main__":
    try:
        sys.exit(main())
    except Exception, e:
        syslog.syslog(syslog.LOG_ERR,
                      "Error running mavericks-auth-upgrade script, %s" % e)

