#!/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()

[((879, 889), 'os.path.dirname', 'dirname', (['p'], {}), '(p)\n', (886, 889), False, 'from os.path import basename, dirname, exists, realpath\n'), ((3408, 3435), 'os.open', 'os.open', (['os.devnull', 'O_RDWR'], {}), '(os.devnull, O_RDWR)\n', (3415, 3435), False, 'import os\n'), ((3476, 3488), 'os.close', 'os.close', (['fd'], {}), '(fd)\n', (3484, 3488), False, 'import os\n'), ((3607, 3653), 'os.open', 'os.open', (['f', '(O_WRONLY | O_APPEND | O_CREAT)', '(420)'], {}), '(f, O_WRONLY | O_APPEND | O_CREAT, 420)\n', (3614, 3653), False, 'import os\n'), ((4348, 4362), 'os.path.exists', 'exists', (['source'], {}), '(source)\n', (4354, 4362), False, 'from os.path import basename, dirname, exists, realpath\n'), ((5605, 5631), 'os.path.exists', 'exists', (['options.log_levels'], {}), '(options.log_levels)\n', (5611, 5631), False, 'from os.path import basename, dirname, exists, realpath\n'), ((6233, 6275), 'os.path.join', 'pathjoin', (['options.install_path', '"""lib"""', '"""*"""'], {}), "(options.install_path, 'lib', '*')\n", (6241, 6275), True, 'from os.path import join as pathjoin\n'), ((6506, 6523), 'os.environ.copy', 'os.environ.copy', ([], {}), '()\n', (6521, 6523), False, 'import os\n'), ((7257, 7283), 'os.chdir', 'os.chdir', (['options.data_dir'], {}), '(options.data_dir)\n', (7265, 7283), False, 'import os\n'), ((7358, 7388), 'os.execvpe', 'os.execvpe', (['args[0]', 'args', 'env'], {}), '(args[0], args, env)\n', (7368, 7388), False, 'import os\n'), ((7730, 7756), 'os.chdir', 'os.chdir', (['options.data_dir'], {}), '(options.data_dir)\n', (7738, 7756), False, 'import os\n'), ((7768, 7777), 'os.fork', 'os.fork', ([], {}), '()\n', (7775, 7777), False, 'import os\n'), ((8097, 8108), 'os.setsid', 'os.setsid', ([], {}), '()\n', (8106, 8108), False, 'import os\n'), ((8171, 8184), 'os.close', 'os.close', (['log'], {}), '(log)\n', (8179, 8184), False, 'import os\n'), ((8190, 8220), 'os.execvpe', 'os.execvpe', (['args[0]', 'args', 'env'], {}), '(args[0], args, env)\n', (8200, 8220), False, 'import os\n'), ((9593, 9688), 'optparse.OptionParser', 'OptionParser', ([], {'prog': '"""launcher"""', 'usage': '"""usage: %prog [options] command"""', 'description': 'commands'}), "(prog='launcher', usage='usage: %prog [options] command',\n    description=commands)\n", (9605, 9688), False, 'from optparse import OptionParser\n'), ((13325, 13346), 'os.path.exists', 'exists', (['o.node_config'], {}), '(o.node_config)\n', (13331, 13346), False, 'from os.path import basename, dirname, exists, realpath\n'), ((13475, 13531), 'os.path.realpath', 'realpath', (['(options.data_dir or data_dir or o.install_path)'], {}), '(options.data_dir or data_dir or o.install_path)\n', (13483, 13531), False, 'from os.path import basename, dirname, exists, realpath\n'), ((591, 602), 'os.path.basename', 'basename', (['f'], {}), '(f)\n', (599, 602), False, 'from os.path import basename, dirname, exists, realpath\n'), ((732, 742), 'os.path.dirname', 'dirname', (['f'], {}), '(f)\n', (739, 742), False, 'from os.path import basename, dirname, exists, realpath\n'), ((751, 762), 'os.path.basename', 'basename', (['p'], {}), '(p)\n', (759, 762), False, 'from os.path import basename, dirname, exists, realpath\n'), ((979, 993), 'os.makedirs', 'os.makedirs', (['p'], {}), '(p)\n', (990, 993), False, 'import os\n'), ((1666, 1693), 'fcntl.flock', 'flock', (['f', '(LOCK_EX | LOCK_NB)'], {}), '(f, LOCK_EX | LOCK_NB)\n', (1671, 1693), False, 'from fcntl import flock, LOCK_EX, LOCK_NB\n'), ((1927, 1961), 'os.open', 'os.open', (['f', '(O_RDWR | O_CREAT)', 'mode'], {}), '(f, O_RDWR | O_CREAT, mode)\n', (1934, 1961), False, 'import os\n'), ((3452, 3470), 'sys.stdin.fileno', 'sys.stdin.fileno', ([], {}), '()\n', (3468, 3470), False, 'import sys\n'), ((3757, 3776), 'sys.stdout.fileno', 'sys.stdout.fileno', ([], {}), '()\n', (3774, 3776), False, 'import sys\n'), ((3794, 3813), 'sys.stderr.fileno', 'sys.stderr.fileno', ([], {}), '()\n', (3811, 3813), False, 'import sys\n'), ((3937, 3948), 'os.lstat', 'os.lstat', (['p'], {}), '(p)\n', (3945, 3948), False, 'import os\n'), ((4323, 4340), 'os.remove', 'os.remove', (['target'], {}), '(target)\n', (4332, 4340), False, 'import os\n'), ((4372, 4398), 'os.symlink', 'os.symlink', (['source', 'target'], {}), '(source, target)\n', (4382, 4398), False, 'import os\n'), ((4683, 4716), 'os.path.join', 'pathjoin', (['options.data_dir', '"""etc"""'], {}), "(options.data_dir, 'etc')\n", (4691, 4716), True, 'from os.path import join as pathjoin\n'), ((5052, 5079), 'os.path.exists', 'exists', (['options.config_path'], {}), '(options.config_path)\n', (5058, 5079), False, 'from os.path import basename, dirname, exists, realpath\n'), ((5168, 5194), 'os.path.exists', 'exists', (['options.jvm_config'], {}), '(options.jvm_config)\n', (5174, 5194), False, 'from os.path import basename, dirname, exists, realpath\n'), ((5286, 5317), 'os.path.exists', 'exists', (['options.launcher_config'], {}), '(options.launcher_config)\n', (5292, 5317), False, 'from os.path import basename, dirname, exists, realpath\n'), ((6756, 6831), 'os.path.join', 'pathjoin', (['options.install_path', '"""bin"""', '"""procname"""', 'system', '"""libprocname.so"""'], {}), "(options.install_path, 'bin', 'procname', system, 'libprocname.so')\n", (6764, 6831), True, 'from os.path import join as pathjoin\n'), ((6843, 6855), 'os.path.exists', 'exists', (['shim'], {}), '(shim)\n', (6849, 6855), False, 'from os.path import basename, dirname, exists, realpath\n'), ((7307, 7318), 'os.getpid', 'os.getpid', ([], {}), '()\n', (7316, 7318), False, 'import os\n'), ((7619, 7648), 'os.path.dirname', 'dirname', (['options.launcher_log'], {}), '(options.launcher_log)\n', (7626, 7648), False, 'from os.path import basename, dirname, exists, realpath\n'), ((8663, 8673), 'time.sleep', 'sleep', (['(0.1)'], {}), '(0.1)\n', (8668, 8673), False, 'from time import sleep\n'), ((8926, 8951), 'sys.exit', 'sys.exit', (['LSB_NOT_RUNNING'], {}), '(LSB_NOT_RUNNING)\n', (8934, 8951), False, 'import sys\n'), ((2033, 2046), 'os.path.dirname', 'dirname', (['path'], {}), '(path)\n', (2040, 2046), False, 'from os.path import basename, dirname, exists, realpath\n'), ((2645, 2660), 'os.kill', 'os.kill', (['pid', '(0)'], {}), '(pid, 0)\n', (2652, 2660), False, 'import os\n'), ((3964, 3983), 'stat.S_ISLNK', 'S_ISLNK', (['st.st_mode'], {}), '(st.st_mode)\n', (3971, 3983), False, 'from stat import S_ISLNK\n'), ((4783, 4816), 'os.path.join', 'pathjoin', (['options.data_dir', '"""etc"""'], {}), "(options.data_dir, 'etc')\n", (4791, 4816), True, 'from os.path import join as pathjoin\n'), ((4904, 4944), 'os.path.join', 'pathjoin', (['options.install_path', '"""plugin"""'], {}), "(options.install_path, 'plugin')\n", (4912, 4944), True, 'from os.path import join as pathjoin\n'), ((4958, 4994), 'os.path.join', 'pathjoin', (['options.data_dir', '"""plugin"""'], {}), "(options.data_dir, 'plugin')\n", (4966, 4994), True, 'from os.path import join as pathjoin\n'), ((5446, 5472), 'os.path.exists', 'exists', (['options.log_levels'], {}), '(options.log_levels)\n', (5452, 5472), False, 'from os.path import basename, dirname, exists, realpath\n'), ((6722, 6740), 'platform.machine', 'platform.machine', ([], {}), '()\n', (6738, 6740), False, 'import platform\n'), ((8408, 8428), 'os.kill', 'os.kill', (['pid', 'signal'], {}), '(pid, signal)\n', (8415, 8428), False, 'import os\n'), ((12444, 12472), 'sys.exit', 'sys.exit', (['LSB_STATUS_UNKNOWN'], {}), '(LSB_STATUS_UNKNOWN)\n', (12452, 12472), False, 'import sys\n'), ((12618, 12669), 'os.path.join', 'pathjoin', (['o.install_path', '"""bin/launcher.properties"""'], {}), "(o.install_path, 'bin/launcher.properties')\n", (12626, 12669), True, 'from os.path import join as pathjoin\n'), ((12715, 12746), 'os.path.join', 'pathjoin', (['o.install_path', '"""etc"""'], {}), "(o.install_path, 'etc')\n", (12723, 12746), True, 'from os.path import join as pathjoin\n'), ((12800, 12838), 'os.path.join', 'pathjoin', (['o.etc_dir', '"""node.properties"""'], {}), "(o.etc_dir, 'node.properties')\n", (12808, 12838), True, 'from os.path import join as pathjoin\n'), ((12890, 12923), 'os.path.join', 'pathjoin', (['o.etc_dir', '"""jvm.config"""'], {}), "(o.etc_dir, 'jvm.config')\n", (12898, 12923), True, 'from os.path import join as pathjoin\n'), ((12972, 13012), 'os.path.join', 'pathjoin', (['o.etc_dir', '"""config.properties"""'], {}), "(o.etc_dir, 'config.properties')\n", (12980, 13012), True, 'from os.path import join as pathjoin\n'), ((13069, 13106), 'os.path.join', 'pathjoin', (['o.etc_dir', '"""log.properties"""'], {}), "(o.etc_dir, 'log.properties')\n", (13077, 13106), True, 'from os.path import join as pathjoin\n'), ((13197, 13218), 'os.path.exists', 'exists', (['o.node_config'], {}), '(o.node_config)\n', (13203, 13218), False, 'from os.path import basename, dirname, exists, realpath\n'), ((13579, 13623), 'os.path.join', 'pathjoin', (['o.data_dir', '"""var/run/launcher.pid"""'], {}), "(o.data_dir, 'var/run/launcher.pid')\n", (13587, 13623), True, 'from os.path import join as pathjoin\n'), ((13684, 13728), 'os.path.join', 'pathjoin', (['o.data_dir', '"""var/log/launcher.log"""'], {}), "(o.data_dir, 'var/log/launcher.log')\n", (13692, 13728), True, 'from os.path import join as pathjoin\n'), ((13785, 13827), 'os.path.join', 'pathjoin', (['o.data_dir', '"""var/log/server.log"""'], {}), "(o.data_dir, 'var/log/server.log')\n", (13793, 13827), True, 'from os.path import join as pathjoin\n'), ((14316, 14344), 'sys.exit', 'sys.exit', (['LSB_STATUS_UNKNOWN'], {}), '(LSB_STATUS_UNKNOWN)\n', (14324, 14344), False, 'import sys\n'), ((6696, 6713), 'platform.system', 'platform.system', ([], {}), '()\n', (6711, 6713), False, 'import platform\n'), ((14237, 14258), 'traceback.print_exc', 'traceback.print_exc', ([], {}), '()\n', (14256, 14258), False, 'import traceback\n'), ((701, 712), 'os.path.basename', 'basename', (['f'], {}), '(f)\n', (709, 712), False, 'from os.path import basename, dirname, exists, realpath\n'), ((854, 865), 'os.path.basename', 'basename', (['p'], {}), '(p)\n', (862, 865), False, 'from os.path import basename, dirname, exists, realpath\n')]