#!/usr/bin/env python

import errno
import os
import platform
import sys
import traceback

from fcntl import flock, LOCK_EX, LOCK_NB
from optparse import OptionParser
from os import O_RDWR, O_CREAT, O_WRONLY, O_APPEND
from os.path import basename, dirname, exists, realpath
from os.path import join as pathjoin
from signal import SIGTERM, SIGKILL
from stat import S_ISLNK
from time import sleep

COMMANDS = ['run', 'start', 'stop', 'restart', 'kill', 'status']

LSB_NOT_RUNNING = 3
LSB_STATUS_UNKNOWN = 4


def find_install_path(f):
    """Find canonical parent of bin/launcher.py"""
    if basename(f) != 'launcher.py':
        raise Exception("Expected file '%s' to be 'launcher.py' not '%s'" % (f, basename(f)))
    p = realpath(dirname(f))
    if basename(p) != 'bin':
        raise Exception("Expected file '%s' directory to be 'bin' not '%s" % (f, basename(p)))
    return dirname(p)


def makedirs(p):
    """Create directory and all intermediate ones"""
    try:
        os.makedirs(p)
    except OSError as e:
        if e.errno != errno.EEXIST:
            raise


def load_properties(f):
    """Load key/value pairs from a file"""
    properties = {}
    for line in load_lines(f):
        k, v = line.split('=', 1)
        properties[k.strip()] = v.strip()
    return properties


def load_lines(f):
    """Load lines from a file, ignoring blank or comment lines"""
    lines = []
    for line in open(f, 'r').readlines():
        line = line.strip()
        if len(line) > 0 and not line.startswith('#'):
            lines.append(line)
    return lines


def try_lock(f):
    """Try to open an exclusive lock (inheritable) on a file"""
    try:
        flock(f, LOCK_EX | LOCK_NB)
        return True
    except (IOError, OSError):  # IOError in Python 2, OSError in Python 3.
        return False


def open_read_write(f, mode):
    """Open file in read/write mode (without truncating it)"""
    return os.fdopen(os.open(f, O_RDWR | O_CREAT, mode), 'r+')


class Process:
    def __init__(self, path):
        makedirs(dirname(path))
        self.path = path
        self.pid_file = open_read_write(path, 0o600)
        self.refresh()

    def refresh(self):
        self.locked = try_lock(self.pid_file)

    def clear_pid(self):
        assert self.locked, 'pid file not locked by us'
        self.pid_file.seek(0)
        self.pid_file.truncate()

    def write_pid(self, pid):
        self.clear_pid()
        self.pid_file.write(str(pid) + '\n')
        self.pid_file.flush()

    def alive(self):
        self.refresh()
        if self.locked:
            return False

        pid = self.read_pid()
        try:
            os.kill(pid, 0)
            return True
        except OSError as e:
            raise Exception('Signaling pid %s failed: %s' % (pid, e))

    def read_pid(self):
        assert not self.locked, 'pid file is locked by us'
        self.pid_file.seek(0)
        line = self.pid_file.readline().strip()
        if len(line) == 0:
            raise Exception("Pid file '%s' is empty" % self.path)

        try:
            pid = int(line)
        except ValueError:
            raise Exception("Pid file '%s' contains garbage: %s" % (self.path, line))
        if pid <= 0:
            raise Exception("Pid file '%s' contains an invalid pid: %s" % (self.path, pid))
        return pid


def redirect_stdin_to_devnull():
    """Redirect stdin to /dev/null"""
    fd = os.open(os.devnull, O_RDWR)
    os.dup2(fd, sys.stdin.fileno())
    os.close(fd)


def open_append(f):
    """Open a raw file descriptor in append mode"""
    # noinspection PyTypeChecker
    return os.open(f, O_WRONLY | O_APPEND | O_CREAT, 0o644)


def redirect_output(fd):
    """Redirect stdout and stderr to a file descriptor"""
    os.dup2(fd, sys.stdout.fileno())
    os.dup2(fd, sys.stderr.fileno())


def symlink_exists(p):
    """Check if symlink exists and raise if another type of file exists"""
    try:
        st = os.lstat(p)
        if not S_ISLNK(st.st_mode):
            raise Exception('Path exists and is not a symlink: %s' % p)
        return True
    except OSError as e:
        if e.errno != errno.ENOENT:
            raise
    return False


def create_symlink(source, target):
    """Create a symlink, removing the target first if it is a symlink"""
    if symlink_exists(target):
        os.remove(target)
    if exists(source):
        os.symlink(source, target)


def create_app_symlinks(options):
    """
    Symlink the 'etc' and 'plugin' directory into the data directory.

    This is needed to support programs that reference 'etc/xyz' from within
    their config files: log.levels-file=etc/log.properties
    """
    if options.etc_dir != pathjoin(options.data_dir, 'etc'):
        create_symlink(
            options.etc_dir,
            pathjoin(options.data_dir, 'etc'))

    if options.install_path != options.data_dir:
        create_symlink(
            pathjoin(options.install_path, 'plugin'),
            pathjoin(options.data_dir, 'plugin'))


def build_java_execution(options, daemon):
    if not exists(options.config_path):
        raise Exception('Config file is missing: %s' % options.config_path)
    if not exists(options.jvm_config):
        raise Exception('JVM config file is missing: %s' % options.jvm_config)
    if not exists(options.launcher_config):
        raise Exception('Launcher config file is missing: %s' % options.launcher_config)
    if options.log_levels_set and not exists(options.log_levels):
        raise Exception('Log levels file is missing: %s' % options.log_levels)

    properties = options.properties.copy()

    if exists(options.log_levels):
        properties['log.levels-file'] = options.log_levels

    if daemon:
        properties['log.output-file'] = options.server_log
        properties['log.enable-console'] = 'false'

    jvm_properties = load_lines(options.jvm_config)
    launcher_properties = load_properties(options.launcher_config)

    try:
        main_class = launcher_properties['main-class']
    except KeyError:
        raise Exception("Launcher config is missing 'main-class' property")

    properties['config'] = options.config_path

    system_properties = ['-D%s=%s' % i for i in properties.items()]
    classpath = pathjoin(options.install_path, 'lib', '*')

    command = ['java', '-cp', classpath]
    command += jvm_properties + system_properties
    command += [main_class]
    command += options.arguments

    if options.verbose:
        print(command)
        print("")

    env = os.environ.copy()

    # set process name: https://github.com/electrum/procname
    process_name = launcher_properties.get('process-name', '')
    if len(process_name) > 0:
        system = platform.system() + '-' + platform.machine()
        shim = pathjoin(options.install_path, 'bin', 'procname', system, 'libprocname.so')
        if exists(shim):
            env['LD_PRELOAD'] = (env.get('LD_PRELOAD', '') + ':' + shim).strip()
            env['PROCNAME'] = process_name

    return command, env


def run(process, options):
    if process.alive():
        print('Already running as %s' % process.read_pid())
        return

    create_app_symlinks(options)
    args, env = build_java_execution(options, False)

    makedirs(options.data_dir)
    os.chdir(options.data_dir)

    process.write_pid(os.getpid())

    redirect_stdin_to_devnull()

    os.execvpe(args[0], args, env)


def start(process, options):
    if process.alive():
        print('Already running as %s' % process.read_pid())
        return

    create_app_symlinks(options)
    args, env = build_java_execution(options, True)

    makedirs(dirname(options.launcher_log))
    log = open_append(options.launcher_log)

    makedirs(options.data_dir)
    os.chdir(options.data_dir)

    pid = os.fork()
    if pid > 0:
        process.write_pid(pid)
        print('Started as %s' % pid)
        return

    if hasattr(os, "set_inheritable"):
        # See https://docs.python.org/3/library/os.html#inheritance-of-file-descriptors
        # Since Python 3.4
        os.set_inheritable(process.pid_file.fileno(), True)

    os.setsid()

    redirect_stdin_to_devnull()
    redirect_output(log)
    os.close(log)

    os.execvpe(args[0], args, env)


def terminate(process, signal, message):
    if not process.alive():
        print('Not running')
        return

    pid = process.read_pid()

    while True:
        try:
            os.kill(pid, signal)
        except OSError as e:
            if e.errno != errno.ESRCH:
                raise Exception('Signaling pid %s failed: %s' % (pid, e))

        if not process.alive():
            process.clear_pid()
            break

        sleep(0.1)

    print('%s %s' % (message, pid))


def stop(process):
    terminate(process, SIGTERM, 'Stopped')


def kill(process):
    terminate(process, SIGKILL, 'Killed')


def status(process):
    if not process.alive():
        print('Not running')
        sys.exit(LSB_NOT_RUNNING)
    print('Running as %s' % process.read_pid())


def handle_command(command, options):
    process = Process(options.pid_file)
    if command == 'run':
        run(process, options)
    elif command == 'start':
        start(process, options)
    elif command == 'stop':
        stop(process)
    elif command == 'restart':
        stop(process)
        start(process, options)
    elif command == 'kill':
        kill(process)
    elif command == 'status':
        status(process)
    else:
        raise AssertionError('Unhandled command: ' + command)


def create_parser():
    commands = 'Commands: ' + ', '.join(COMMANDS)
    parser = OptionParser(prog='launcher', usage='usage: %prog [options] command', description=commands)
    parser.add_option('-v', '--verbose', action='store_true', default=False, help='Run verbosely')
    parser.add_option('--etc-dir', metavar='DIR', help='Defaults to INSTALL_PATH/etc')
    parser.add_option('--launcher-config', metavar='FILE', help='Defaults to INSTALL_PATH/bin/launcher.properties')
    parser.add_option('--node-config', metavar='FILE', help='Defaults to ETC_DIR/node.properties')
    parser.add_option('--jvm-config', metavar='FILE', help='Defaults to ETC_DIR/jvm.config')
    parser.add_option('--config', metavar='FILE', help='Defaults to ETC_DIR/config.properties')
    parser.add_option('--log-levels-file', metavar='FILE', help='Defaults to ETC_DIR/log.properties')
    parser.add_option('--data-dir', metavar='DIR', help='Defaults to INSTALL_PATH')
    parser.add_option('--pid-file', metavar='FILE', help='Defaults to DATA_DIR/var/run/launcher.pid')
    parser.add_option('--arg', action='append', metavar='ARG', dest='arguments', help='Add a program argument of the Java application')
    parser.add_option('--launcher-log-file', metavar='FILE', help='Defaults to DATA_DIR/var/log/launcher.log (only in daemon mode)')
    parser.add_option('--server-log-file', metavar='FILE', help='Defaults to DATA_DIR/var/log/server.log (only in daemon mode)')
    parser.add_option('-D', action='append', metavar='NAME=VALUE', dest='properties', help='Set a Java system property')
    return parser


def parse_properties(parser, args):
    properties = {}
    for arg in args:
        if '=' not in arg:
            parser.error('property is malformed: %s' % arg)
        key, value = [i.strip() for i in arg.split('=', 1)]
        if key == 'config':
            parser.error('cannot specify config using -D option (use --config)')
        if key == 'log.output-file':
            parser.error('cannot specify server log using -D option (use --server-log-file)')
        if key == 'log.levels-file':
            parser.error('cannot specify log levels using -D option (use --log-levels-file)')
        properties[key] = value
    return properties


def print_options(options):
    if options.verbose:
        for i in sorted(vars(options)):
            print("%-15s = %s" % (i, getattr(options, i)))
        print("")


class Options:
    pass


def main():
    parser = create_parser()

    (options, args) = parser.parse_args()

    if len(args) != 1:
        if len(args) == 0:
            parser.error('command name not specified')
        else:
            parser.error('too many arguments')
    command = args[0]

    if command not in COMMANDS:
        parser.error('unsupported command: %s' % command)

    try:
        install_path = find_install_path(sys.argv[0])
    except Exception as e:
        print('ERROR: %s' % e)
        sys.exit(LSB_STATUS_UNKNOWN)

    o = Options()
    o.verbose = options.verbose
    o.install_path = install_path
    o.launcher_config = realpath(options.launcher_config or pathjoin(o.install_path, 'bin/launcher.properties'))
    o.etc_dir = realpath(options.etc_dir or pathjoin(o.install_path, 'etc'))
    o.node_config = realpath(options.node_config or pathjoin(o.etc_dir, 'node.properties'))
    o.jvm_config = realpath(options.jvm_config or pathjoin(o.etc_dir, 'jvm.config'))
    o.config_path = realpath(options.config or pathjoin(o.etc_dir, 'config.properties'))
    o.log_levels = realpath(options.log_levels_file or pathjoin(o.etc_dir, 'log.properties'))
    o.log_levels_set = bool(options.log_levels_file)

    if options.node_config and not exists(o.node_config):
        parser.error('Node config file is missing: %s' % o.node_config)

    node_properties = {}
    if exists(o.node_config):
        node_properties = load_properties(o.node_config)

    data_dir = node_properties.get('node.data-dir')
    o.data_dir = realpath(options.data_dir or data_dir or o.install_path)

    o.pid_file = realpath(options.pid_file or pathjoin(o.data_dir, 'var/run/launcher.pid'))
    o.launcher_log = realpath(options.launcher_log_file or pathjoin(o.data_dir, 'var/log/launcher.log'))
    o.server_log = realpath(options.server_log_file or pathjoin(o.data_dir, 'var/log/server.log'))

    o.properties = parse_properties(parser, options.properties or {})
    for k, v in node_properties.items():
        if k not in o.properties:
            o.properties[k] = v

    o.arguments = options.arguments or []

    if o.verbose:
        print_options(o)

    try:
        handle_command(command, o)
    except SystemExit:
        raise
    except Exception as e:
        if o.verbose:
            traceback.print_exc()
        else:
            print('ERROR: %s' % e)
        sys.exit(LSB_STATUS_UNKNOWN)


if __name__ == '__main__':
    main()

[((31, 11, 31, 21), 'os.path.dirname', 'dirname', ({(31, 19, 31, 20): 'p'}, {}), '(p)', False, 'from os.path import basename, dirname, exists, realpath\n'), ((126, 9, 126, 36), 'os.open', 'os.open', ({(126, 17, 126, 27): 'os.devnull', (126, 29, 126, 35): 'O_RDWR'}, {}), '(os.devnull, O_RDWR)', False, 'import os\n'), ((128, 4, 128, 16), 'os.close', 'os.close', ({(128, 13, 128, 15): 'fd'}, {}), '(fd)', False, 'import os\n'), ((134, 11, 134, 59), 'os.open', 'os.open', ({(134, 19, 134, 20): 'f', (134, 22, 134, 51): '(O_WRONLY | O_APPEND | O_CREAT)', (134, 53, 134, 58): '(420)'}, {}), '(f, O_WRONLY | O_APPEND | O_CREAT, 420)', False, 'import os\n'), ((160, 7, 160, 21), 'os.path.exists', 'exists', ({(160, 14, 160, 20): 'source'}, {}), '(source)', False, 'from os.path import basename, dirname, exists, realpath\n'), ((194, 7, 194, 33), 'os.path.exists', 'exists', ({(194, 14, 194, 32): 'options.log_levels'}, {}), '(options.log_levels)', False, 'from os.path import basename, dirname, exists, realpath\n'), ((212, 16, 212, 58), 'os.path.join', 'pathjoin', ({(212, 25, 212, 45): 'options.install_path', (212, 47, 212, 52): '"""lib"""', (212, 54, 212, 57): '"""*"""'}, {}), "(options.install_path, 'lib', '*')", True, 'from os.path import join as pathjoin\n'), ((223, 10, 223, 27), 'os.environ.copy', 'os.environ.copy', ({}, {}), '()', False, 'import os\n'), ((246, 4, 246, 30), 'os.chdir', 'os.chdir', ({(246, 13, 246, 29): 'options.data_dir'}, {}), '(options.data_dir)', False, 'import os\n'), ((252, 4, 252, 34), 'os.execvpe', 'os.execvpe', ({(252, 15, 252, 22): 'args[0]', (252, 24, 252, 28): 'args', (252, 30, 252, 33): 'env'}, {}), '(args[0], args, env)', False, 'import os\n'), ((267, 4, 267, 30), 'os.chdir', 'os.chdir', ({(267, 13, 267, 29): 'options.data_dir'}, {}), '(options.data_dir)', False, 'import os\n'), ((269, 10, 269, 19), 'os.fork', 'os.fork', ({}, {}), '()', False, 'import os\n'), ((280, 4, 280, 15), 'os.setsid', 'os.setsid', ({}, {}), '()', False, 'import os\n'), ((284, 4, 284, 17), 'os.close', 'os.close', ({(284, 13, 284, 16): 'log'}, {}), '(log)', False, 'import os\n'), ((286, 4, 286, 34), 'os.execvpe', 'os.execvpe', ({(286, 15, 286, 22): 'args[0]', (286, 24, 286, 28): 'args', (286, 30, 286, 33): 'env'}, {}), '(args[0], args, env)', False, 'import os\n'), ((348, 13, 348, 104), 'optparse.OptionParser', 'OptionParser', (), '', False, 'from optparse import OptionParser\n'), ((428, 7, 428, 28), 'os.path.exists', 'exists', ({(428, 14, 428, 27): 'o.node_config'}, {}), '(o.node_config)', False, 'from os.path import basename, dirname, exists, realpath\n'), ((432, 17, 432, 73), 'os.path.realpath', 'realpath', ({(432, 26, 432, 72): 'options.data_dir or data_dir or o.install_path'}, {}), '(options.data_dir or data_dir or o.install_path)', False, 'from os.path import basename, dirname, exists, realpath\n'), ((26, 7, 26, 18), 'os.path.basename', 'basename', ({(26, 16, 26, 17): 'f'}, {}), '(f)', False, 'from os.path import basename, dirname, exists, realpath\n'), ((28, 17, 28, 27), 'os.path.dirname', 'dirname', ({(28, 25, 28, 26): 'f'}, {}), '(f)', False, 'from os.path import basename, dirname, exists, realpath\n'), ((29, 7, 29, 18), 'os.path.basename', 'basename', ({(29, 16, 29, 17): 'p'}, {}), '(p)', False, 'from os.path import basename, dirname, exists, realpath\n'), ((37, 8, 37, 22), 'os.makedirs', 'os.makedirs', ({(37, 20, 37, 21): 'p'}, {}), '(p)', False, 'import os\n'), ((65, 8, 65, 35), 'fcntl.flock', 'flock', ({(65, 14, 65, 15): 'f', (65, 17, 65, 34): '(LOCK_EX | LOCK_NB)'}, {}), '(f, LOCK_EX | LOCK_NB)', False, 'from fcntl import flock, LOCK_EX, LOCK_NB\n'), ((73, 21, 73, 55), 'os.open', 'os.open', ({(73, 29, 73, 30): 'f', (73, 32, 73, 48): '(O_RDWR | O_CREAT)', (73, 50, 73, 54): 'mode'}, {}), '(f, O_RDWR | O_CREAT, mode)', False, 'import os\n'), ((127, 16, 127, 34), 'sys.stdin.fileno', 'sys.stdin.fileno', ({}, {}), '()', False, 'import sys\n'), ((139, 16, 139, 35), 'sys.stdout.fileno', 'sys.stdout.fileno', ({}, {}), '()', False, 'import sys\n'), ((140, 16, 140, 35), 'sys.stderr.fileno', 'sys.stderr.fileno', ({}, {}), '()', False, 'import sys\n'), ((146, 13, 146, 24), 'os.lstat', 'os.lstat', ({(146, 22, 146, 23): 'p'}, {}), '(p)', False, 'import os\n'), ((159, 8, 159, 25), 'os.remove', 'os.remove', ({(159, 18, 159, 24): 'target'}, {}), '(target)', False, 'import os\n'), ((161, 8, 161, 34), 'os.symlink', 'os.symlink', ({(161, 19, 161, 25): 'source', (161, 27, 161, 33): 'target'}, {}), '(source, target)', False, 'import os\n'), ((171, 26, 171, 59), 'os.path.join', 'pathjoin', ({(171, 35, 171, 51): 'options.data_dir', (171, 53, 171, 58): '"""etc"""'}, {}), "(options.data_dir, 'etc')", True, 'from os.path import join as pathjoin\n'), ((183, 11, 183, 38), 'os.path.exists', 'exists', ({(183, 18, 183, 37): 'options.config_path'}, {}), '(options.config_path)', False, 'from os.path import basename, dirname, exists, realpath\n'), ((185, 11, 185, 37), 'os.path.exists', 'exists', ({(185, 18, 185, 36): 'options.jvm_config'}, {}), '(options.jvm_config)', False, 'from os.path import basename, dirname, exists, realpath\n'), ((187, 11, 187, 42), 'os.path.exists', 'exists', ({(187, 18, 187, 41): 'options.launcher_config'}, {}), '(options.launcher_config)', False, 'from os.path import basename, dirname, exists, realpath\n'), ((229, 15, 229, 90), 'os.path.join', 'pathjoin', ({(229, 24, 229, 44): 'options.install_path', (229, 46, 229, 51): '"""bin"""', (229, 53, 229, 63): '"""procname"""', (229, 65, 229, 71): 'system', (229, 73, 229, 89): '"""libprocname.so"""'}, {}), "(options.install_path, 'bin', 'procname', system, 'libprocname.so')", True, 'from os.path import join as pathjoin\n'), ((230, 11, 230, 23), 'os.path.exists', 'exists', ({(230, 18, 230, 22): 'shim'}, {}), '(shim)', False, 'from os.path import basename, dirname, exists, realpath\n'), ((248, 22, 248, 33), 'os.getpid', 'os.getpid', ({}, {}), '()', False, 'import os\n'), ((263, 13, 263, 42), 'os.path.dirname', 'dirname', ({(263, 21, 263, 41): 'options.launcher_log'}, {}), '(options.launcher_log)', False, 'from os.path import basename, dirname, exists, realpath\n'), ((307, 8, 307, 18), 'time.sleep', 'sleep', ({(307, 14, 307, 17): '(0.1)'}, {}), '(0.1)', False, 'from time import sleep\n'), ((323, 8, 323, 33), 'sys.exit', 'sys.exit', ({(323, 17, 323, 32): 'LSB_NOT_RUNNING'}, {}), '(LSB_NOT_RUNNING)', False, 'import sys\n'), ((78, 17, 78, 30), 'os.path.dirname', 'dirname', ({(78, 25, 78, 29): 'path'}, {}), '(path)', False, 'from os.path import basename, dirname, exists, realpath\n'), ((103, 12, 103, 27), 'os.kill', 'os.kill', ({(103, 20, 103, 23): 'pid', (103, 25, 103, 26): '(0)'}, {}), '(pid, 0)', False, 'import os\n'), ((147, 15, 147, 34), 'stat.S_ISLNK', 'S_ISLNK', ({(147, 23, 147, 33): 'st.st_mode'}, {}), '(st.st_mode)', False, 'from stat import S_ISLNK\n'), ((174, 12, 174, 45), 'os.path.join', 'pathjoin', ({(174, 21, 174, 37): 'options.data_dir', (174, 39, 174, 44): '"""etc"""'}, {}), "(options.data_dir, 'etc')", True, 'from os.path import join as pathjoin\n'), ((178, 12, 178, 52), 'os.path.join', 'pathjoin', ({(178, 21, 178, 41): 'options.install_path', (178, 43, 178, 51): '"""plugin"""'}, {}), "(options.install_path, 'plugin')", True, 'from os.path import join as pathjoin\n'), ((179, 12, 179, 48), 'os.path.join', 'pathjoin', ({(179, 21, 179, 37): 'options.data_dir', (179, 39, 179, 47): '"""plugin"""'}, {}), "(options.data_dir, 'plugin')", True, 'from os.path import join as pathjoin\n'), ((189, 38, 189, 64), 'os.path.exists', 'exists', ({(189, 45, 189, 63): 'options.log_levels'}, {}), '(options.log_levels)', False, 'from os.path import basename, dirname, exists, realpath\n'), ((228, 43, 228, 61), 'platform.machine', 'platform.machine', ({}, {}), '()', False, 'import platform\n'), ((298, 12, 298, 32), 'os.kill', 'os.kill', ({(298, 20, 298, 23): 'pid', (298, 25, 298, 31): 'signal'}, {}), '(pid, signal)', False, 'import os\n'), ((411, 8, 411, 36), 'sys.exit', 'sys.exit', ({(411, 17, 411, 35): 'LSB_STATUS_UNKNOWN'}, {}), '(LSB_STATUS_UNKNOWN)', False, 'import sys\n'), ((416, 60, 416, 111), 'os.path.join', 'pathjoin', ({(416, 69, 416, 83): 'o.install_path', (416, 85, 416, 110): '"""bin/launcher.properties"""'}, {}), "(o.install_path, 'bin/launcher.properties')", True, 'from os.path import join as pathjoin\n'), ((417, 44, 417, 75), 'os.path.join', 'pathjoin', ({(417, 53, 417, 67): 'o.install_path', (417, 69, 417, 74): '"""etc"""'}, {}), "(o.install_path, 'etc')", True, 'from os.path import join as pathjoin\n'), ((418, 52, 418, 90), 'os.path.join', 'pathjoin', ({(418, 61, 418, 70): 'o.etc_dir', (418, 72, 418, 89): '"""node.properties"""'}, {}), "(o.etc_dir, 'node.properties')", True, 'from os.path import join as pathjoin\n'), ((419, 50, 419, 83), 'os.path.join', 'pathjoin', ({(419, 59, 419, 68): 'o.etc_dir', (419, 70, 419, 82): '"""jvm.config"""'}, {}), "(o.etc_dir, 'jvm.config')", True, 'from os.path import join as pathjoin\n'), ((420, 47, 420, 87), 'os.path.join', 'pathjoin', ({(420, 56, 420, 65): 'o.etc_dir', (420, 67, 420, 86): '"""config.properties"""'}, {}), "(o.etc_dir, 'config.properties')", True, 'from os.path import join as pathjoin\n'), ((421, 55, 421, 92), 'os.path.join', 'pathjoin', ({(421, 64, 421, 73): 'o.etc_dir', (421, 75, 421, 91): '"""log.properties"""'}, {}), "(o.etc_dir, 'log.properties')", True, 'from os.path import join as pathjoin\n'), ((424, 35, 424, 56), 'os.path.exists', 'exists', ({(424, 42, 424, 55): 'o.node_config'}, {}), '(o.node_config)', False, 'from os.path import basename, dirname, exists, realpath\n'), ((434, 46, 434, 90), 'os.path.join', 'pathjoin', ({(434, 55, 434, 65): 'o.data_dir', (434, 67, 434, 89): '"""var/run/launcher.pid"""'}, {}), "(o.data_dir, 'var/run/launcher.pid')", True, 'from os.path import join as pathjoin\n'), ((435, 59, 435, 103), 'os.path.join', 'pathjoin', ({(435, 68, 435, 78): 'o.data_dir', (435, 80, 435, 102): '"""var/log/launcher.log"""'}, {}), "(o.data_dir, 'var/log/launcher.log')", True, 'from os.path import join as pathjoin\n'), ((436, 55, 436, 97), 'os.path.join', 'pathjoin', ({(436, 64, 436, 74): 'o.data_dir', (436, 76, 436, 96): '"""var/log/server.log"""'}, {}), "(o.data_dir, 'var/log/server.log')", True, 'from os.path import join as pathjoin\n'), ((457, 8, 457, 36), 'sys.exit', 'sys.exit', ({(457, 17, 457, 35): 'LSB_STATUS_UNKNOWN'}, {}), '(LSB_STATUS_UNKNOWN)', False, 'import sys\n'), ((228, 17, 228, 34), 'platform.system', 'platform.system', ({}, {}), '()', False, 'import platform\n'), ((454, 12, 454, 33), 'traceback.print_exc', 'traceback.print_exc', ({}, {}), '()', False, 'import traceback\n'), ((27, 80, 27, 91), 'os.path.basename', 'basename', ({(27, 89, 27, 90): 'f'}, {}), '(f)', False, 'from os.path import basename, dirname, exists, realpath\n'), ((30, 81, 30, 92), 'os.path.basename', 'basename', ({(30, 90, 30, 91): 'p'}, {}), '(p)', False, 'from os.path import basename, dirname, exists, realpath\n')]