Back up a sqlite3 database, while ensuring
    that no ongoing queries are being executed.

    Return True on success
    Return False on error.

def sqlite3_backup(src_path, dest_path):
    """
    Back up a sqlite3 database, while ensuring
    that no ongoing queries are being executed.

    Return True on success
    Return False on error.
    """

    # find sqlite3
    sqlite3_path = sqlite3_find_tool()
    if sqlite3_path is None:
        log.error("Failed to find sqlite3 tool")
        return False

    sqlite3_cmd = [sqlite3_path, '{}'.format(src_path), '.backup "{}"'.format(dest_path)]
    rc = None
    backoff = 1.0

    out = None
    err = None

    try:
        while True:
            log.debug("{}".format(" ".join(sqlite3_cmd)))
            p = subprocess.Popen(sqlite3_cmd, shell=False, close_fds=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
            out, err = p.communicate()
            rc = p.wait()

            if rc != 0:
                if "database is locked" in out.lower() or "database is locked" in err.lower():
                    # try again
                    log.error("Database {} is locked; trying again in {} seconds".format(src_path, backoff))
                    time.sleep(backoff)
                    backoff += 2 * backoff + random.random() * random.randint(0, int(backoff))
                    continue

                elif 'is not a database' in out.lower() or 'is not a database' in err.lower():
                    # not a valid sqlite3 file
                    log.error("File {} is not a SQLite database".format(src_path))
                    return False

                else:
                    # some other failure.  Try again
                    log.error('Failed to back up with "{}".  Error log follows.\n{}'.format(" ".join(sqlite3_cmd), err))
                    continue
            else:
                break

    except Exception, e:
        log.exception(e)
        return False

    if not os.WIFEXITED(rc):
        # bad exit 
        # failed for some other reason
        log.error("Backup failed: out='{}', err='{}', rc={}".format(out, err, rc))
        return False
    
    if os.WEXITSTATUS(rc) != 0:
        # bad exit
        log.error("Backup failed: out='{}', err='{}', exit={}".format(out, err, os.WEXITSTATUS(rc)))
        return False

    return True