#!/usr/bin/python

## # db access spec: http://www.python.org/dev/peps/pep-0249/
## # ...

# md5 stuff
import md5
## >>> import md5
## >>> mmdd = md5.new("some string or whatever")
## >>> dir(mmdd)
## ['copy', 'digest', 'hexdigest', 'update']
## >>> mmdd.hexdigest()
## 'c89b653491ae4f5d849954a3fc2091c2'
## del(mmdd)

## # commandline args
## >>> import sys
## >>> sys.argv <--is a list

## # to do this... datestr = "YYYY-MM-DD hh:mm:ss"
## >>> import time
## >>> datestr = time.strftime("%Y-%M-%d %H:%M:%S")
## >>> datestr
## '2007-47-02 20:47:08'

# logging locations
logDir =  "/var/log/dbmon"
logFile = "dbmon.log"
errFile = "dbmon.err"
sqlFile = "dbmon.sql"

# how many milliseconds to sleep in our "show full processlist" loop
#   ms    # per second
#   ---   ------------
#   500 = 2/sec
#   200 = 5/sec
#   100 = 10/sec
#   50  = 20/sec
#   25  = 40/sec
#   10  = 100/sec
# This is only an estimate, because the algorithm is a simple sleep for
# X milliseconds between each run. If the run takes 100 milliseconds, the
# actual run interval will be X+100 milliseconds.
interval = 25 * 1000


daemon = 0
killLongRunning = 0
noKillVolatile = 1
killConn = 0
doRotate = 0


from optparse import OptionParser
parser = OptionParser()
# -h is implied! python is teh r0000l1!11111!eleventy
parser.add_option("-d", "--daemon", dest="daemon", action="store_true", default=False,
                  help="daemonise the script")
parser.add_option("-t", "--terminal", dest="daemon", action="store_false", default=False,
                  help="run the script in a terminal (STDOUT/STDERR)")
parser.add_option("-l", "--license", dest="showlicense", action="store_true", default=False,
                  help="show the license for this script")
parser.add_option("-k", "--killlong", dest="killLongRunning", default=0,
                  help="kill long-running queries that go longer than N seconds")
parser.add_option("-o", "--killvolatile", dest="noKillVolatile", action="store_false", default=True,
                  help="kill long-running queries are volatile (eg INSERT, UPDATE, DELETE, specifically, non-SELECT)")


## # following is Perl - make it Python
## if (arg =~ /^-+killconn$/)       { $killConn = 1; } #...
## if (arg =~ /^-+rotate$/)         { $doRotate = 1; } #...


if (showlicense):
    doLicense()
    exit(0)


# we'll overwrite this from time to time
currTime = time.strftime('%Y-%m-%d %H:%M:%S')


# kill existing monitor
import logging
logging.info(" - Looking for existing monitor...")


# I do this in Perl ALL THE FSCKING TIME
import commands
pid = commands.getoutput("""ps auxww | grep 'MySQL Query Monitor' | grep -v grep | head -1 | awk '{print $2;}'""")


killCount = 0
while (pid and killCount <= 5):
    logging.info(" + Killing existing monitor...")
    killout = commands.getoutput("kill -2 %d" % (pid))
    usleep(200000)
    pid = commands.getoutput("""ps auxww | grep 'MySQL Query Monitor' | grep -v grep | head -1 | awk '{print $2;}'""")
    if (pid):
        killout = commands.getoutput("kill -9 %d" % (pid))
    usleep(50000)
    pid = commands.getoutput("""ps auxww | grep 'MySQL Query Monitor' | grep -v grep | head -1 | awk '{print $2;}'""")

    killcount += 1


if (killCount > 4):
    logging.info(" - Had trouble killing old monitor. Check logs at %s" % logDir
    exit(255)


# rotate out old log info if user requests it
if (doRotate):
    print " - Rotating out log information\n"
    doRotate("dbmon.err")
    doRotate("dbmon.sql")
    doRotate("dbmon.log")


# daemonise!!!
if (daemon) {
    logging.info(" - Daemonising...")

    # make the logdir if it doesn't exist
    mkdir "$logDir" unless (-d "$logDir")

    close (STDIN)
    close (STDOUT)
    close (STDERR)

    open (STDIN, "</dev/null")
    open (STDERR, ">>$logDir/$errFile")
    open (STDOUT, ">>$logDir/$logFile")
    open (SQLLOGFILE, ">>$logDir/$sqlFile")

    # set unbuffered output. it is not an accident that
    # i choose to select STDOUT last.
    select SQLLOGFILE; $| = 1
    select STDERR; $| = 1
    select STDOUT; $| = 1

    print STDOUT     "\n - Statistics Messages will go into $logDir/$logFile at $currTime\n"
    print STDOUT     " - MD5 hash-->SQL mapping is in $logDir/$sqlFile\n"
    print STDERR     "\n - Error Messages will go into $logDir/$errFile at $currTime\n"
    print SQLLOGFILE "\nmd5-hash\tquery-text\n"

    exit (0) if (fork())
    POSIX::setsid()
    exit (0) if (fork())
    $0 = "$0 killLongRunning=$killLongRunning MySQL Query Monitor - see $logDir/$logFile"
}

# make database connection
dbh
sth

&connectDb()

my %queryDetails
my %connectionDetails
digest

while (1) {
    $sth->execute()
    if ($DBI::errstr) {
        warn "Database error: $DBI::errstr\n"

        # make sure we're connected
        $dbh->disconnect()
        &connectDb()
    }

    my @this_dbid = ()

    # loop through all processes
    while (my @row = $sth->fetchrow()) {
        $currTime = strftime ('%Y-%m-%d %H:%M:%S', localtime)

        my ($dbid, $user, $host, $dbname, $state, $time, $action, $query) = map { defined $_ ? $_ : "undef" } @row

        # don't pay any attention to these administrative queries
        next if ($user eq "System User")
        next if ($state eq "Sleep" || $state eq "Connect")
        next if ($query eq "undef" || $query eq "show full processlist")

        # make the query easier to view in logs
        $query =~ s/[\t\n\r]/ /g

        # if, in comments, there's a [queryName=Name] or [queryID=id] then use
        # that for the hash, not the full query
        if ($query =~ /\[queryName=(.+?)\]/ || $query =~ /\[queryID=(.+?)\]/) {
            $digest = md5_hex($1)
        } else {
            $digest = md5_hex($query)
        }

        # i think just the dbid is enough to uniquely identify the query for
        # future identification and killing
        detailsKey = $dbid

        # store the list of currently-running queries
        push @this_dbid, $query

        # logic based on a query we know about
        if (exists $queryDetails{$detailsKey}) {
            # change in state of query
            if ($state ne $queryDetails{$detailsKey}{'state'}) {
                printf STDOUT $queryDetails{$detailsKey}{'digest'} 
                . "\tState\t$currTime\t$user\@$host\t$state\tid=" 
                . $queryDetails{$detailsKey}{'dbid'} . "\t"
                . "%3.3f sec\n", gettimeofday() - $queryDetails{$detailsKey}{'time'}
                $queryDetails{$detailsKey}{'state'} = $state
            }

            # kill long-running queries but only attempt this every 5 seconds
            killFlag = 0
            connTime = 0
            if (exists $connectionDetails{$detailsKey}) { $connTime = gettimeofday() - $connectionDetails{$detailsKey}; }
            if (($killLongRunning && ($time >= $killLongRunning) && (($time - $killLongRunning) % 5 == 0)) 
                ||
                ($killLongRunning && ($connTime >= $killLongRunning) && (($connTime - $killLongRunning) % 5 == 0))
               ) {
                # only kill queries with case-insensitive "select "
                # at the beginning, discounting /*comment*/ stuff
                # in them if we are only killing nonvolatile queries
                if ($noKillVolatile && $query !~ /^(\/\*.+\*\/)*\s*select /i) {
                    print STDERR " - $currTime :: Not killing volatile query. digest=$digest query=$query\n"
                } elsif ($query =~ /allowtorun=(\d+)/) {
                    secondsAllowed = $1
                    if ($time > $secondsAllowed) {
                        $killFlag = 1
                    } else {
                        print STDERR " - $currTime :: 'allowtorun=$secondsAllowed' > seconds=$time. Not killing. digest=$digest query=$query\n"
                    }
                } else {
                    $killFlag = 1
                }
               
                if ($killFlag) {
                    print STDERR " - $currTime :: Attempting to kill digest=$digest query=$query\n"
                    $dbh->do ("kill $dbid")
                    print STDOUT "$digest\tAutoKll\t$currTime\t$user\@$host\t$state\tid=$dbid\n"
                }
            }
        } else {
            $queryDetails{$detailsKey}{'query'}  = $query
            $queryDetails{$detailsKey}{'time'}   = gettimeofday()
            $queryDetails{$detailsKey}{'state'}  = $state
            $queryDetails{$detailsKey}{'user'}   = $user
            $queryDetails{$detailsKey}{'host'}   = $host
            $queryDetails{$detailsKey}{'dbid'}   = $dbid
            $queryDetails{$detailsKey}{'digest'} = $digest

            # get the info about the connection
            unless ($connectionDetails{$detailsKey}) { $connectionDetails{$detailsKey} = gettimeofday(); }
            ## we need to figure out where we can do this - can't do it at query
            ## end, because the connection doesn't end there. :( - need to do something
            ## like the grep below on the donequery bit, but for all connections, not
            ## just connections doing something.
            #delete $connectionDetails{$queryKey}

            print STDOUT "$digest\tStart\t$currTime\t$user\@$host\t$state\tid=$dbid\n"
            if ($daemon) {
                print SQLLOGFILE "$digest\t$query\n"
            } else {
                print STDERR "$digest\t$query\n"
            }
        }

    }

    # look through queries checking for done ones
    for queryKey (keys %queryDetails) {
        $digest = $queryDetails{$queryKey}{'digest'}
        user = $queryDetails{$queryKey}{'user'}
        host = $queryDetails{$queryKey}{'host'}
        state = $queryDetails{$queryKey}{'state'}

        # this is a done query -- write out stats about it
        if (! scalar grep ( { $queryDetails{$queryKey}{'query'} eq $_ } @this_dbid)) {
            $currTime = strftime ('%Y-%m-%d %H:%M:%S', localtime)
            printf STDOUT "$digest\tFinish\t$currTime\t$user\@$host\t$state\tid="
            . $queryDetails{$queryKey}{'dbid'} . "\t"
            . "%3.3f sec\n", gettimeofday() - $queryDetails{$queryKey}{'time'}
            delete $queryDetails{$queryKey}
        }
    }
    usleep $interval
}


sub doRotate {
    fileName = shift
    moveTo
    idx

    print "   - $fileName\n"
    if (-e "$logDir/$fileName") {
        foreach $idx (5,4,3,2,1,0) {
            if (-e "$logDir/$fileName.$idx") {
                $moveTo = $idx + 1
                if (-e "$logDir/$fileName.$moveTo") { unlink "$logDir/$fileName.$moveTo"; }
                rename ("$logDir/$fileName.$idx", "$logDir/$fileName.$moveTo")
            }
        }
        rename ("$logDir/$fileName", "$logDir/$fileName.0")
    }
}


sub connectDb {
    print STDERR " - $currTime :: Attempting to connect to database $dbMachine\n"
    $dbh = DBI->connect("dbi:mysql:mysql:$dbMachine", "$dbUser", "$dbPass", { RaiseError => 0})
    # we're going to run this command over and over millions of times
    eval { $sth = $dbh->prepare("show full processlist"); }

    while ($@) {
        # give the database a little breathing room whilst we try to reconnect
        # forever
        print STDERR " - $currTime :: Sleeping 5 seconds before attempting another reconnect\n"
        sleep 5

        $dbh = DBI->connect("dbi:mysql:mysql:$dbMachine", "$dbUser", "$dbPass", { RaiseError => 0})
        # we're going to run this command over and over millions of times
        eval { $sth = $dbh->prepare("show full processlist"); }
    }
}


def doArgs():
    print "\n"
    print "usage: $0 {--daemon|--terminal} [--help] [--rotate]\n"
    print "    [--killlong=N] [--killvolatile] [--killconn] [--license]\n"
    print "Required argument:\n"
    print "  --daemon       run as a daemon, logging into $logDir\n"
    print "  --terminal     run in terminal, STDOUT is statistics, STDERR is queries\n"
    print "Optional arguments:\n"
    print "  --killlong=N   kill >N-second-running things ('thing'==query by default, but can be connection)\n"
    print "  --killvolatile also kill volatile (insert/update/delete) long-running queries\n"
    print "  --killconn     kill long-connected connections, not long-running queries\n"
    print "  --rotate       do stupid (but effective) rotation of logs in $logDir\n"
    print "  --license      how is this program licensed?\n"
    print "  --help         get help\n"
    print "\n"
    print "By default, when killing long-running queries, only SELECTs will be killed.\n"
    print "It can be dangerous to kill volatile queries, since many apps don't expect\n"
    print "that to happen.  If the query has 'allowtorun=N' (N is a number) in a comment,\n"
    print "it can run N seconds before being killed.  When run in --daemon mode, will log\n"
    print "output to logfiles.  Run with --rotate periodically if you don't use\n"
    print "log rotation software on the log files.\n"
}


sub doLicense {
    licenseText

    $licenseText = "\n"
    . qq{ Copyright(c)2005 by Tim Ellis, all rights reserved except as laid out by the \n }
    . qq{ license below. \n }
    . qq{  \n }
    . qq{ GNU GENERAL PUBLIC LICENSE \n }
    . qq{ Version 2, June 1991 \n }
    . qq{  \n }
    . qq{ Copyright (C) 1989, 1991 Free Software Foundation, Inc.  51 Franklin Street, \n }
    . qq{ Fifth Floor, Boston, MA  02110-1301, USA \n }
    . qq{  \n }
    . qq{ Everyone is permitted to copy and distribute verbatim copies of this license \n }
    . qq{ document, but changing it is not allowed. \n }
    . qq{  \n }
    . qq{ Preamble \n }
    . qq{  \n }
    . qq{ The licenses for most software are designed to take away your freedom to share \n }
    . qq{ and change it. By contrast, the GNU General Public License is intended to \n }
    . qq{ guarantee your freedom to share and change free software--to make sure the \n }
    . qq{ software is free for all its users. This General Public License applies to most \n }
    . qq{ of the Free Software Foundation's software and to any other program whose \n }
    . qq{ authors commit to using it. (Some other Free Software Foundation software is \n }
    . qq{ covered by the GNU Lesser General Public License instead.) You can apply it to \n }
    . qq{ your programs, too. \n }
    . qq{  \n }
    . qq{ When we speak of free software, we are referring to freedom, not price. Our \n }
    . qq{ General Public Licenses are designed to make sure that you have the freedom to \n }
    . qq{ distribute copies of free software (and charge for this service if you wish), \n }
    . qq{ that you receive source code or can get it if you want it, that you can change \n }
    . qq{ the software or use pieces of it in new free programs; and that you know you \n }
    . qq{ can do these things. \n }
    . qq{  \n }
    . qq{ To protect your rights, we need to make restrictions that forbid anyone to deny \n }
    . qq{ you these rights or to ask you to surrender the rights. These restrictions \n }
    . qq{ translate to certain responsibilities for you if you distribute copies of the \n }
    . qq{ software, or if you modify it. \n }
    . qq{  \n }
    . qq{ For example, if you distribute copies of such a program, whether gratis or for \n }
    . qq{ a fee, you must give the recipients all the rights that you have. You must make \n }
    . qq{ sure that they, too, receive or can get the source code. And you must show them \n }
    . qq{ these terms so they know their rights. \n }
    . qq{  \n }
    . qq{ We protect your rights with two steps: (1) copyright the software, and (2) \n }
    . qq{ offer you this license which gives you legal permission to copy, distribute \n }
    . qq{ and/or modify the software. \n }
    . qq{  \n }
    . qq{ Also, for each author's protection and ours, we want to make certain that \n }
    . qq{ everyone understands that there is no warranty for this free software. If the \n }
    . qq{ software is modified by someone else and passed on, we want its recipients to \n }
    . qq{ know that what they have is not the original, so that any problems introduced \n }
    . qq{ by others will not reflect on the original authors' reputations. \n }
    . qq{  \n }
    . qq{ Finally, any free program is threatened constantly by software patents. We wish \n }
    . qq{ to avoid the danger that redistributors of a free program will individually \n }
    . qq{ obtain patent licenses, in effect making the program proprietary. To prevent \n }
    . qq{ this, we have made it clear that any patent must be licensed for everyone's \n }
    . qq{ free use or not licensed at all. \n }
    . qq{  \n }
    . qq{ The precise terms and conditions for copying, distribution and modification \n }
    . qq{ follow.  TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION \n }
    . qq{  \n }
    . qq{ 0. This License applies to any program or other work which contains a notice \n }
    . qq{ placed by the copyright holder saying it may be distributed under the terms of \n }
    . qq{ this General Public License. The "Program", below, refers to any such program \n }
    . qq{ or work, and a "work based on the Program" means either the Program or any \n }
    . qq{ derivative work under copyright law: that is to say, a work containing the \n }
    . qq{ Program or a portion of it, either verbatim or with modifications and/or \n }
    . qq{ translated into another language. (Hereinafter, translation is included without \n }
    . qq{ limitation in the term "modification".) Each licensee is addressed as "you". \n }
    . qq{  \n }
    . qq{ Activities other than copying, distribution and modification are not covered by \n }
    . qq{ this License; they are outside its scope. The act of running the Program is not \n }
    . qq{ restricted, and the output from the Program is covered only if its contents \n }
    . qq{ constitute a work based on the Program (independent of having been made by \n }
    . qq{ running the Program). Whether that is true depends on what the Program does. \n }
    . qq{  \n }
    . qq{ 1. You may copy and distribute verbatim copies of the Program's source code as \n }
    . qq{ you receive it, in any medium, provided that you conspicuously and \n }
    . qq{ appropriately publish on each copy an appropriate copyright notice and \n }
    . qq{ disclaimer of warranty; keep intact all the notices that refer to this License \n }
    . qq{ and to the absence of any warranty; and give any other recipients of the \n }
    . qq{ Program a copy of this License along with the Program. \n }
    . qq{  \n }
    . qq{ You may charge a fee for the physical act of transferring a copy, and you may \n }
    . qq{ at your option offer warranty protection in exchange for a fee. \n }
    . qq{  \n }
    . qq{ 2. You may modify your copy or copies of the Program or any portion of it, thus \n }
    . qq{ forming a work based on the Program, and copy and distribute such modifications \n }
    . qq{ or work under the terms of Section 1 above, provided that you also meet all of \n }
    . qq{ these conditions: \n }
    . qq{  \n }
    . qq{ a) You must cause the modified files to carry prominent notices stating that \n }
    . qq{ you changed the files and the date of any change.  \n }
    . qq{  \n }
    . qq{ b) You must cause any work that you distribute or publish, that in whole or in \n }
    . qq{ part contains or is derived from the Program or any part thereof, to be \n }
    . qq{ licensed as a whole at no charge to all third parties under the terms of this \n }
    . qq{ License.  \n }
    . qq{  \n }
    . qq{ c) If the modified program normally reads commands interactively when run, you \n }
    . qq{ must cause it, when started running for such interactive use in the most \n }
    . qq{ ordinary way, to print or display an announcement including an appropriate \n }
    . qq{ copyright notice and a notice that there is no warranty (or else, saying that \n }
    . qq{ you provide a warranty) and that users may redistribute the program under these \n }
    . qq{ conditions, and telling the user how to view a copy of this License. \n }
    . qq{ (Exception: if the Program itself is interactive but does not normally print \n }
    . qq{ such an announcement, your work based on the Program is not required to print \n }
    . qq{ an announcement.)  \n }
    . qq{  \n }
    . qq{ These requirements apply to the modified work as a whole. If identifiable \n }
    . qq{ sections of that work are not derived from the Program, and can be reasonably \n }
    . qq{ considered independent and separate works in themselves, then this License, and \n }
    . qq{ its terms, do not apply to those sections when you distribute them as separate \n }
    . qq{ works. But when you distribute the same sections as part of a whole which is a \n }
    . qq{ work based on the Program, the distribution of the whole must be on the terms \n }
    . qq{ of this License, whose permissions for other licensees extend to the entire \n }
    . qq{ whole, and thus to each and every part regardless of who wrote it. \n }
    . qq{  \n }
    . qq{ Thus, it is not the intent of this section to claim rights or contest your \n }
    . qq{ rights to work written entirely by you; rather, the intent is to exercise the \n }
    . qq{ right to control the distribution of derivative or collective works based on \n }
    . qq{ the Program. \n }
    . qq{  \n }
    . qq{ In addition, mere aggregation of another work not based on the Program with the \n }
    . qq{ Program (or with a work based on the Program) on a volume of a storage or \n }
    . qq{ distribution medium does not bring the other work under the scope of this \n }
    . qq{ License. \n }
    . qq{  \n }
    . qq{ 3. You may copy and distribute the Program (or a work based on it, under \n }
    . qq{ Section 2) in object code or executable form under the terms of Sections 1 and \n }
    . qq{ 2 above provided that you also do one of the following: \n }
    . qq{  \n }
    . qq{ a) Accompany it with the complete corresponding machine-readable source code, \n }
    . qq{ which must be distributed under the terms of Sections 1 and 2 above on a medium \n }
    . qq{ customarily used for software interchange; or,  \n }
    . qq{  \n }
    . qq{ b) Accompany it with a written offer, valid for at least three years, to give \n }
    . qq{ any third party, for a charge no more than your cost of physically performing \n }
    . qq{ source distribution, a complete machine-readable copy of the corresponding \n }
    . qq{ source code, to be distributed under the terms of Sections 1 and 2 above on a \n }
    . qq{ medium customarily used for software interchange; or,  \n }
    . qq{  \n }
    . qq{ c) Accompany it with the information you received as to the offer to distribute \n }
    . qq{ corresponding source code. (This alternative is allowed only for noncommercial \n }
    . qq{ distribution and only if you received the program in object code or executable \n }
    . qq{ form with such an offer, in accord with Subsection b above.)  \n }
    . qq{  \n }
    . qq{ The source code for a work means the preferred form of the work for making \n }
    . qq{ modifications to it. For an executable work, complete source code means all the \n }
    . qq{ source code for all modules it contains, plus any associated interface \n }
    . qq{ definition files, plus the scripts used to control compilation and installation \n }
    . qq{ of the executable. However, as a special exception, the source code distributed \n }
    . qq{ need not include anything that is normally distributed (in either source or \n }
    . qq{ binary form) with the major components (compiler, kernel, and so on) of the \n }
    . qq{ operating system on which the executable runs, unless that component itself \n }
    . qq{ accompanies the executable. \n }
    . qq{  \n }
    . qq{ If distribution of executable or object code is made by offering access to copy \n }
    . qq{ from a designated place, then offering equivalent access to copy the source \n }
    . qq{ code from the same place counts as distribution of the source code, even though \n }
    . qq{ third parties are not compelled to copy the source along with the object code. \n }
    . qq{  \n }
    . qq{ 4. You may not copy, modify, sublicense, or distribute the Program except as \n }
    . qq{ expressly provided under this License. Any attempt otherwise to copy, modify, \n }
    . qq{ sublicense or distribute the Program is void, and will automatically terminate \n }
    . qq{ your rights under this License. However, parties who have received copies, or \n }
    . qq{ rights, from you under this License will not have their licenses terminated so \n }
    . qq{ long as such parties remain in full compliance. \n }
    . qq{  \n }
    . qq{ 5. You are not required to accept this License, since you have not signed it. \n }
    . qq{ However, nothing else grants you permission to modify or distribute the Program \n }
    . qq{ or its derivative works. These actions are prohibited by law if you do not \n }
    . qq{ accept this License. Therefore, by modifying or distributing the Program (or \n }
    . qq{ any work based on the Program), you indicate your acceptance of this License to \n }
    . qq{ do so, and all its terms and conditions for copying, distributing or modifying \n }
    . qq{ the Program or works based on it. \n }
    . qq{  \n }
    . qq{ 6. Each time you redistribute the Program (or any work based on the Program), \n }
    . qq{ the recipient automatically receives a license from the original licensor to \n }
    . qq{ copy, distribute or modify the Program subject to these terms and conditions. \n }
    . qq{ You may not impose any further restrictions on the recipients' exercise of the \n }
    . qq{ rights granted herein. You are not responsible for enforcing compliance by \n }
    . qq{ third parties to this License. \n }
    . qq{  \n }
    . qq{ 7. If, as a consequence of a court judgment or allegation of patent \n }
    . qq{ infringement or for any other reason (not limited to patent issues), conditions \n }
    . qq{ are imposed on you (whether by court order, agreement or otherwise) that \n }
    . qq{ contradict the conditions of this License, they do not excuse you from the \n }
    . qq{ conditions of this License. If you cannot distribute so as to satisfy \n }
    . qq{ simultaneously your obligations under this License and any other pertinent \n }
    . qq{ obligations, then as a consequence you may not distribute the Program at all. \n }
    . qq{ For example, if a patent license would not permit royalty-free redistribution \n }
    . qq{ of the Program by all those who receive copies directly or indirectly through \n }
    . qq{ you, then the only way you could satisfy both it and this License would be to \n }
    . qq{ refrain entirely from distribution of the Program. \n }
    . qq{  \n }
    . qq{ If any portion of this section is held invalid or unenforceable under any \n }
    . qq{ particular circumstance, the balance of the section is intended to apply and \n }
    . qq{ the section as a whole is intended to apply in other circumstances. \n }
    . qq{  \n }
    . qq{ It is not the purpose of this section to induce you to infringe any patents or \n }
    . qq{ other property right claims or to contest validity of any such claims; this \n }
    . qq{ section has the sole purpose of protecting the integrity of the free software \n }
    . qq{ distribution system, which is implemented by public license practices. Many \n }
    . qq{ people have made generous contributions to the wide range of software \n }
    . qq{ distributed through that system in reliance on consistent application of that \n }
    . qq{ system; it is up to the author/donor to decide if he or she is willing to \n }
    . qq{ distribute software through any other system and a licensee cannot impose that \n }
    . qq{ choice. \n }
    . qq{  \n }
    . qq{ This section is intended to make thoroughly clear what is believed to be a \n }
    . qq{ consequence of the rest of this License. \n }
    . qq{  \n }
    . qq{ 8. If the distribution and/or use of the Program is restricted in certain \n }
    . qq{ countries either by patents or by copyrighted interfaces, the original \n }
    . qq{ copyright holder who places the Program under this License may add an explicit \n }
    . qq{ geographical distribution limitation excluding those countries, so that \n }
    . qq{ distribution is permitted only in or among countries not thus excluded. In such \n }
    . qq{ case, this License incorporates the limitation as if written in the body of \n }
    . qq{ this License. \n }
    . qq{  \n }
    . qq{ 9. The Free Software Foundation may publish revised and/or new versions of the \n }
    . qq{ General Public License from time to time. Such new versions will be similar in \n }
    . qq{ spirit to the present version, but may differ in detail to address new problems \n }
    . qq{ or concerns. \n }
    . qq{  \n }
    . qq{ Each version is given a distinguishing version number. If the Program specifies \n }
    . qq{ a version number of this License which applies to it and "any later version", \n }
    . qq{ you have the option of following the terms and conditions either of that \n }
    . qq{ version or of any later version published by the Free Software Foundation. If \n }
    . qq{ the Program does not specify a version number of this License, you may choose \n }
    . qq{ any version ever published by the Free Software Foundation. \n }
    . qq{  \n }
    . qq{ 10. If you wish to incorporate parts of the Program into other free programs \n }
    . qq{ whose distribution conditions are different, write to the author to ask for \n }
    . qq{ permission. For software which is copyrighted by the Free Software Foundation, \n }
    . qq{ write to the Free Software Foundation; we sometimes make exceptions for this. \n }
    . qq{ Our decision will be guided by the two goals of preserving the free status of \n }
    . qq{ all derivatives of our free software and of promoting the sharing and reuse of \n }
    . qq{ software generally. \n }
    . qq{  \n }
    . qq{ NO WARRANTY \n }
    . qq{  \n }
    . qq{ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR \n }
    . qq{ THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE \n }
    . qq{ STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE \n }
    . qq{ PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, \n }
    . qq{ INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND \n }
    . qq{ FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND \n }
    . qq{ PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU \n }
    . qq{ ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. \n }
    . qq{  \n }
    . qq{ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL \n }
    . qq{ ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE \n }
    . qq{ PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY \n }
    . qq{ GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR \n }
    . qq{ INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA \n }
    . qq{ BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A \n }
    . qq{ FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER \n }
    . qq{ OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. \n }


    print $licenseText
}