#! /opt/imh-python/bin/python
""" Scan a URL, giving details about the process. """

import re
import sys
import os
import pwd
import logging
from argparse import ArgumentParser
from rads.common import colors
from rads.common import setup_logging
from strace_tools import strace_url
import json


LOGGER = logging.getLogger(__name__)

__author__ = "Daniel K"
__email__ = "danielk@inmotionhosting.com"


def colorize(text, color, end='none'):
    """Apply a color to text data"""
    return '%s%s%s' % (colors()[color], text, colors()[end])


def set_logging(is_quiet, verbosity, output_file):
    ''' Setup the logging in a separate function '''
    if is_quiet:
        logging_level = logging.CRITICAL
    else:
        if None is verbosity:
            logging_level = logging.ERROR
        elif 1 == verbosity:
            logging_level = logging.WARNING
        elif 2 == verbosity:
            logging_level = logging.INFO
        elif 3 == verbosity:
            logging_level = logging.DEBUG
        else:
            logging_level = logging.DEBUG

    if output_file == '':
        setup_logging(loglevel=logging_level, print_out=sys.stderr)
    else:
        setup_logging(
            logfile=output_file,
            loglevel=logging_level,
            print_out=False
        )


def parse_args():
    '''
        Parse command line arguments
    '''

    parser = ArgumentParser(description=__doc__)

    parser.add_argument(
        "-v", "--verbose", action='count',
        help="Print verbose output. May be added multiple times."
    )

    parser.add_argument(
        "-q", "--quiet", action='store_true',
        help="Do not output logging. Overrides -v."
    )

    parser.add_argument(
        "-o", "--output", action='store', type=str, default='',
        help="Output logging to the specified file."
    )

    output_parser_group = parser.add_argument_group("Output options")
    output_group = output_parser_group.add_mutually_exclusive_group()

    output_group.add_argument(
        "-r", "--raw", action='store_true',
        help="Output strace raw data."
    )

    output_group.add_argument(
        "-s", "--search", type=str, default=None,
        help="Search for SEARCH in file and socket reads and writes."
    )

    output_group.add_argument(
        "-d", "--debug", action='store_true',
        help="Print verbose diagnostic information."
    )

    output_group.add_argument(
        "-e", "--errors", action='store_true',
        help="Output data sent to error logs."
    )

    output_group.add_argument(
        "-m", "--mysql", action='store_true',
        help="Show read and writes to local mysql socket."
    )

    output_group.add_argument(
        "-n", "--netio", action='store_true',
        help="Show network read, writes, and timeouts."
    )

    output_group.add_argument(
        "-f", "--files", action='store_true',
        help="Show file activity."
    )


    parser.add_argument(
        'url', metavar='URL', type=str, nargs='?',
        help=(
            "Path to begin searching for CMS. "
            "If none is given, assume the current directory"
        )
    )

    args = parser.parse_args()

    if args.url is None:
        print "URL not given"
        sys.exit(1)


    set_logging(args.quiet, args.verbose, args.output)

    return args.url, args.raw, args.search, args.debug, args.mysql, args.netio, args.errors, args.files

def nothing_callback(command, line, strace_handler, additional_data = {}):
    pass

def print_callback(command, line, strace_handler, additional_data = {}):
    print "Command [%s] - %s" % (command, line)

def read_callback(command, line, strace_handler, additional_data = {}):
    print "Read %s bytes from %s:  [[[%s]]]" % (additional_data['size'],additional_data['filename'],additional_data['data'])

def dump_callback(command, line, strace_handler, additional_data = {}):
    print "Command [%s] - %s" % (command, line)
    for key in additional_data:
        print "\t%s: %s" % (key, additional_data[key])
    #dump(additional_data)
    print "\n"

def open_callback(command, line, strace_handler, additional_data = {}):
    if 'success' not in additional_data:
         print "No data?"
         return False


    if additional_data['success']:
        if 'filename' in additional_data:
            if 'fd' in additional_data:
                print "Opened %s on %s" % (additional_data['filename'], additional_data['fd'])
            else:
                print "Opened %s on something" % additional_data['filename']
        else:
            print "Successful open of something"
    else:
        print "Failed open for %s" % additional_data['filename']

def check_for_sock(command, line, strace_handler, additional_data = {}):
    if 'filename' in additional_data:
        if 'mysql.sock' in additional_data['filename']:
            if 'read' == command:
                print "Reading: %s\n" % additional_data['data']
            elif 'write' == command:
                print "Writing: %s\n" % additional_data['data']

TEXT="commit13_"
def search_for_text(command, line, strace_handler, additional_data = {}):
    if 'data' in additional_data:
        if TEXT in additional_data['data']:
            #print "From %s: [[[%s]]]" % (additional_data['filename'],additional_data['data'])
            print additional_data['filename']

def send_receive(command, line, strace_handler, additional_data = {}):
    send_rcv_match = re.search(r'(sendto|recvfrom)\((?P<handle>[0-9]+),\s+"(?P<data>.*)", [0-9]+,',line)
    if send_rcv_match is not None:
        if command == "sendto":
            print "Sending to ",
        else:
            print "Receiving from ",
        handle =  int(send_rcv_match.group('handle'))
        print strace_handler.file_handle[handle].filename,
        print ": ",
        print send_rcv_match.group('data')
        print "\n"

def catch_timeout(command, line, strace_handler, additional_data = {}):
    timeout_match = re.search(r'poll\(\[{fd=(?P<handle>[0-9+]+),\s+events=([A-Z_|]+)}\],\s+[0-9]+,\s+[0-9]+\)\s+=\s+[0-9]+ \(Timeout\)',line)
    if timeout_match is not None:
        handle =  int(timeout_match.group('handle'))
        print "Timeout on %s" % strace_handler.file_handle[handle].filename,
        print "\n"

def print_errors(command, line, strace_handler, additional_data = {}):
    if 'filename' in additional_data:
        if 'STDERR' in additional_data['filename'] or 'error' in additional_data['filename'] or '/dev/null' in additional_data['filename']:
            print "%s: %s\n" % (additional_data['filename'], additional_data['data'])

def command_filename(command, line, strace_handler, additional_data = {}):
    if 'filename' in additional_data:
        print "%s: %s" % (command, additional_data['filename'])

def filename_data_size(command, line, strace_handler, additional_data = {}):
    if 'filename' in additional_data:
        if 'size' in additional_data:
            print "%s %s: %s bytes" % (command, additional_data['filename'], additional_data['size'])

def testing(command, line, strace_handler, additional_data = {}):
    if 'filename' in additional_data:
        return "File named %s" % additional_data['filename']
    return "Just a test"

def handle_exit(command, line, strace_handler, additional_data = {}):
    result = strace_handler.get_stats()
    print "Result: %s" % json.dumps(result, sort_keys=True,indent=4, separators=(',', ': '))

def get_errors(command, line, strace_handler, additional_data = {}):
    if 'filename' in additional_data:
        if (
            ('STDERR!!!' in additional_data['filename']) or
            ('error' in additional_data['filename']) or
            ('/dev/null' in additional_data['filename'])
        ):
            print "%s: %s\n" % (additional_data['filename'], additional_data['data'])



def main():
    ''' Main function for scan_url '''

    (url, raw, search, debug, mysql, netio, errors, files) = parse_args()

    callbacks = {}
    if search is not None:
        global TEXT
        TEXT = search
        callbacks['read'] = search_for_text
        callbacks['write'] = search_for_text
    elif debug:
        #callbacks['default'] = dump_callback
        #callbacks['write'] = testing
        callbacks['end'] = handle_exit
    elif errors:
        callbacks['write'] = print_errors
    elif netio:
        callbacks['sendto'] = send_receive
        callbacks['recvfrom'] = send_receive
        callbacks['poll'] = catch_timeout
    elif mysql:
        callbacks['sendto'] = check_for_sock
        callbacks['recvfrom'] = check_for_sock
        callbacks['read'] = check_for_sock
        callbacks['write'] = check_for_sock
    elif files:
        callbacks['open'] = command_filename
        callbacks['close'] = command_filename
        callbacks['read'] = filename_data_size
        callbacks['write'] = filename_data_size
    else:
        callbacks['write'] = get_errors
        callbacks['poll'] = catch_timeout


    if raw:
        for line in strace_url(url, callbacks, raw):
            print line.rstrip()
    else:
        for result in strace_url(url, callbacks, raw):
            if result is not None:
                print "Result: %s" % json.dumps(result, sort_keys=True,indent=4, separators=(',', ': '))

    #if not raw:
    #    handler.print_stats()


if __name__ == "__main__":
    main()
