Beispiel #1
0
    def interrupt_backup(self):
        '''
            End user interrupts the backup.

            >>> from blockchain_backup.bitcoin.tests import utils as test_utils
            >>> test_utils.init_database()
            >>> backup_task = BackupTask()
            >>> backup_task.manager = BitcoinManager(backup_task.log_name)
            >>> backup_task.prep_backup()
            >>> backup_process, backup_pid = backup_task.start_backup()

            >>> test_utils.start_fake_backup()
            >>> backup_task.interrupt_backup()
            True
        '''

        MAX_SECS = 3

        seconds = 0

        self.log('interrupting backup')
        if self.to_backup_dir is not None:
            # remove all files that suggest this backup is complete
            bitcoin_utils.delete_last_updated_files(self.to_backup_dir)
            # add a flag that we started to use this dir to backup
            self.add_backup_flag()

        try:
            bin_dir = os.path.join(virtualenv_dir(), 'bin')
            args = [
                os.path.join(bin_dir, 'killmatch'), constants.BACKUP_PROGRAM
            ]

            attempts = 0
            while is_backup_running() and attempts < 3:
                result = command.run(*args).stdout
                self.log(f'result of stopping backup: {result}')
                if is_backup_running():
                    sleep(3)
                    attempts += 1
        except CalledProcessError as cpe:
            self.log(cpe)
            self.log(format_exc())

        # a new page was displayed so give socketio time to connect
        while seconds < MAX_SECS:
            self.manager.update_header(self.STOPPED_BACKUP_HEADER)
            self.manager.update_progress(self.STOPPED_BACKUP_PROGRESS)
            self.manager.notify_close_window()

            sleep(1)
            seconds += 1

        # return value is for testing purposes only
        return not bitcoin_utils.is_backup_running()
Beispiel #2
0
    def wait_for_backup(self, backup_process):
        '''
            Wait for the backup to finish and display data while waiting.

            >>> from blockchain_backup.bitcoin.tests import utils as test_utils
            >>> test_utils.init_database()
            >>> backup_task = BackupTask()
            >>> backup_task.manager = BitcoinManager(backup_task.log_name)
            >>> backup_task.prep_backup()
            >>> backup_process, backup_pid = backup_task.start_backup()
            >>> backup_process is not None
            True
            >>> backup_pid is None
            True
            >>> backup_task.wait_for_backup(backup_process)
            >>> test_utils.stop_backup()
            >>> backup_task.wait_for_backup(None)
            >>> test_utils.stop_backup()
        '''
        def show_line(line):
            if line is not None and line.startswith('Copying:'):
                index = line.rfind(os.sep)
                if index > 0:
                    line = self.COPYING.format(line[index + 1:])
                if line != self.COPYING:
                    self.manager.update_progress(line)

        self.log('starting to wait for backup')

        if backup_process is None:
            log_path = os.path.join(BASE_LOG_DIR, whoami(), 'bcb-backup.log')

            # wait until the log appears
            while (is_backup_running() and not self.is_interrupted()):
                if not os.path.exists(log_path):
                    sleep(1)

            # then display the backup details
            while (is_backup_running() and not self.is_interrupted()):
                with open(log_path, 'rt') as backup_log:
                    show_line(backup_log.readline())
        else:
            while (backup_process.poll() is None
                   and not self.is_interrupted()):
                show_line(backup_process.stdout.readline())

        if self.is_interrupted():
            self.log('waiting for backup interrupted')
        else:
            self.log('finished waiting for backup')
Beispiel #3
0
    def check_for_conflicts(self, request):
        ''' Check if there are other apps/tasks running. '''

        response = None

        # check that no other bitcoin-core app is running
        if bitcoin_utils.is_bitcoin_core_running():
            log(f'bitcoind running: {bitcoin_utils.is_bitcoind_running()}')
            log(f'bitcoin_qt running: {bitcoin_utils.is_bitcoin_qt_running()}')
            log(f'bitcoin_tx running: {bitcoin_utils.is_bitcoin_tx_running()}')

            response = warn_core_running(request)

        # tell user if backup is running
        elif bitcoin_utils.is_backup_running():
            log('backup running')
            response = warn_bcb_app_running(request,
                                            app=constants.BACKUP_PROGRAM)

        # tell user if restore is already running
        elif bitcoin_utils.is_restore_running():
            log('restore running')
            response = self.restore()

        # tell user if another task is running
        elif accessing_wallet() or updating() or backing_up():
            log('task running')
            response = warn_bcb_task_running(request)

        return response
    def tearDown(self):

        # make sure nothing is left running
        test_utils.stop_bitcoin_core_apps()
        if is_backup_running():
            log('interrupting backup')
            views.InterruptBackup.as_view()(self.factory.get('/bitcoin/interrupt_backup/'))
            sleep(20)

        # some tests might create a .bitcon in the home dir
        test_utils.delete_home_bitcoin_subdir(self.home_bitcoin_subdir_exists)
Beispiel #5
0
    def stop_backup(self, backup_process, backup_pid):
        '''
            Stop backup.

            >>> from blockchain_backup.bitcoin.tests import utils as test_utils
            >>> test_utils.init_database()
            >>> backup_task = BackupTask()
            >>> backup_task.manager = BitcoinManager(backup_task.log_name)
            >>> backup_task.prep_backup()
            >>> backup_process, backup_pid = backup_task.start_backup()
            >>> backup_process is not None
            True
            >>> backup_pid is None
            True
            >>> backup_task.stop_backup(backup_process, backup_pid)
            >>> test_utils.start_fake_backup()
            >>> backup_pid = get_pid(constants.BACKUP_PROGRAM)
            >>> backup_task.stop_backup(None, backup_pid)
        '''
        try:
            if backup_process is None and backup_pid is not None:
                if is_backup_running():
                    bin_dir = os.path.join(virtualenv_dir(), 'bin')
                    args = [
                        os.path.join(bin_dir, 'killmatch'),
                        '"{} --exclude {}"'.format(constants.BACKUP_PROGRAM,
                                                   get_excluded_files())
                    ]
                    result = command.run(*args).stdout
                    self.log(f'killing backup result: {result}')

                try:
                    pid, returncode = os.waitpid(backup_pid, os.P_WAIT)
                    self.log(f'waitpid {pid} return code: {returncode}')
                except ChildProcessError:
                    self.log('backup_pid already dead')
            else:
                # if bcb-backup hasn't stopped yet, then kill it
                if backup_process is None:
                    self.log('not back process active')
                else:
                    if backup_process.poll() is None:
                        self.log('killing backup')
                        backup_process.terminate()

                    # wait until backup terminates
                    backup_process.wait()
                    self.log(
                        f'backup return code: {backup_process.returncode}')
        except:  # 'bare except' because it catches more than "except Exception"
            self.log(f'error while stopping backup\n{format_exc()}')
    def test_backup_when_backups_disabled(self):
        '''
            Test trying to back up when backups disabled.
        '''

        state.set_backups_enabled(False)

        request = self.factory.get('/bitcoin/backup/')
        response = views.Backup.as_view()(request)
        self.assertEqual(response.status_code, 302)
        self.assertEqual(response.url, '/bitcoin/change_backup_status/')
        self.assertFalse(is_backup_running())

        state.set_backups_enabled(True)
Beispiel #7
0
def stop_backup():
    '''
        Stop the backup.

        >>> init_database()
        >>> stop_backup()
    '''
    while bitcoin_utils.is_backup_running():
        sleep(5)

        bin_dir = os.path.join(virtualenv_dir(), 'bin')
        excluded_files = bitcoin_utils.get_excluded_files()
        args = [os.path.join(bin_dir, 'killmatch'),
                f'"{constants.BACKUP_PROGRAM} --exclude {excluded_files}"']
        result = command.run(*args).stdout
        log(f'killing backup result: {result}')
Beispiel #8
0
    def tearDown(self):

        # make sure nothing is left running
        if is_backup_running():
            InterruptBackup.as_view()(
                self.factory.get('/bitcoin/interrupt_backup/'))
            sleep(20)

        # make sure bitcoin-qt shuts down; 60 secs may not be enough,
        # but don't want to waste time waiting needlessly
        if is_bitcoin_qt_running():
            sleep(60)
            test_utils.stop_bitcoin_qt()

        # some tests might create a .bitcom in the home dir
        test_utils.delete_home_bitcoin_subdir(self.home_bitcoin_subdir_exists)
Beispiel #9
0
    def start_backup(self):
        '''
            Start backup.

            >>> from blockchain_backup.bitcoin.tests import utils as test_utils
            >>> test_utils.init_database()
            >>> backup_task = BackupTask()
            >>> backup_task.manager = BitcoinManager(backup_task.log_name)
            >>> backup_task.prep_backup()
            >>> backup_process, backup_pid = backup_task.start_backup()
            >>> backup_process is not None
            True
            >>> backup_pid is None
            True
            >>> test_utils.stop_backup()
        '''
        bin_dir = os.path.join(virtualenv_dir(), 'bin')
        data_dir = self.manager.data_dir
        if not data_dir.endswith(os.sep):
            data_dir += os.sep

        if is_backup_running():
            backup_process = None
            backup_pid = get_pid(constants.BACKUP_PROGRAM)
            self.log('{} is already running using pid: {}'.format(
                constants.BACKUP_PROGRAM, backup_pid))
        else:
            backup_pid = None

            args = []
            # "bcb-backup" is a link to safecopy so we can distinguish it when we kill it
            args.append(os.path.join(bin_dir, constants.BACKUP_PROGRAM))
            args.append('--exclude')
            args.append(get_excluded_files())
            args.append('--verbose')
            args.append('--quick')
            args.append('--delete')
            args.append(f'{data_dir}*')
            args.append(self.to_backup_dir)

            # Popen appears to report "'list' object has no attribute 'split'"
            # the docs state Popen should pass a sequence as the first arg
            backup_process = Popen(args, stdout=PIPE, universal_newlines=True)

        return backup_process, backup_pid
Beispiel #10
0
def warn_bcb_app_running(request, app=None):
    '''
        Warn that a denova app is running.

        >>> from django.test import RequestFactory
        >>> no_backup_message = bytearray(
        ...   NO_BACKUP_IF_CORE_RUNNING, encoding='utf-8')
        >>> factory = RequestFactory()
        >>> request = factory.get('/bitcoin/preferences/')
        >>> response = warn_bcb_app_running(request)
        >>> response.status_code == 200
        True
        >>> b'Update' in response.content
        True
    '''

    if app is None:
        if bitcoin_utils.is_backup_running():
            app = constants.BACKUP_PROGRAM
        else:
            app = constants.RESTORE_PROGRAM

    return render(request, 'bitcoin/blockchain_backup_app_running.html',
                  {'app': app})
Beispiel #11
0
    def get_page(self, request):

        global update_task

        log('trying to update blockchain')

        clear_action_updates()

        # check that no other bitcoin-core app is running
        if (bitcoin_utils.is_bitcoin_qt_running()
                or bitcoin_utils.is_bitcoin_tx_running()
                or (bitcoin_utils.is_bitcoind_running() and not updating())):
            response = warn_core_running(request)

        # tell user if another blockchain_backup app is running
        elif bitcoin_utils.is_backup_running(
        ) or bitcoin_utils.is_restore_running():
            response = warn_bcb_app_running(request)

        # tell user if another task is running
        elif accessing_wallet() or backing_up() or restoring():
            response = warn_bcb_task_running(request)

        else:
            NOTICE1 = 'WARNING: Don\'t shut down your computer before you <a class="btn btn-secondary " role="button"'
            NOTICE2 = 'id="stop_button" href="/bitcoin/interrupt_update/" title="Click to stop updating the blockchain">Stop update</a>'
            NOTICE = f'{NOTICE1} {NOTICE2}'

            context = bitcoin_utils.get_blockchain_context()

            data_dir_ok, error = preferences.data_dir_ok()
            if not preferences.bin_dir_ok():
                context['notice'] = BAD_BIN_DIR
                log(BAD_BIN_DIR)
            elif not data_dir_ok:
                context['notice'] = BAD_DATA_DIR
                log(error)
            else:
                context['header'] = "Updating Bitcoin's blockchain"
                context[
                    'notice'] = 'WARNING: Don\'t shut down your computer before you <a class="btn btn-secondary " role="button" id="stop_button" href="/bitcoin/interrupt_update/" title="Click to stop updating the blockchain">Stop update</a>'
                context[
                    'subnotice'] = 'Shutting down before this window says it is safe <em>could damage the blockchain</em>.'
                context['progress'] = 'Starting to update the blockchain'
                context['update_interval'] = '5000'

                if updating():
                    log('already updating blockchain')

                else:
                    from blockchain_backup.bitcoin.update import UpdateTask

                    update_task = UpdateTask()
                    update_task.start()
                    log('UpdateTask started')

            # make sure the button doesn't appear any more
            bitcoin_utils.send_socketio_message('button', ' ')

            log(f'context: {context}')
            response = render(request, 'bitcoin/update.html', context=context)

        return response
Beispiel #12
0
    def get_page(self, request):

        global accessing_wallet_task

        log('accessing bitcoin interactively')

        clear_action_updates()

        # it's ok if bitcoin-qt is running in our task
        if (bitcoin_utils.is_bitcoind_running()
                or bitcoin_utils.is_bitcoin_tx_running()
                or (bitcoin_utils.is_bitcoin_qt_running()
                    and not accessing_wallet())):
            response = warn_core_running(request)

        # tell user if another app is running
        elif bitcoin_utils.is_backup_running(
        ) or bitcoin_utils.is_restore_running():
            response = warn_bcb_app_running(request)

        # tell user if another task is running
        elif updating() or backing_up() or restoring():
            response = warn_bcb_task_running(request)

        else:
            SUBNOTICE1 = "Waiting until you exit Bitcoin-QT.<p>Bitcoin-QT will start in another window."
            SUBNOTICE2 = "You can send and receive transactions from that window."
            SUBNOTICE3 = "After you exit Bitcoin-QT, Blockchain Backup will continue updating the blockchain until it's time to back it up."
            SUBNOTICE4 = "Don't forget to back up your wallet routinely."
            SUBNOTICE = f'{SUBNOTICE1} {SUBNOTICE2} {SUBNOTICE3} {SUBNOTICE4}'

            context = bitcoin_utils.get_blockchain_context()

            data_dir_ok, error = preferences.data_dir_ok()
            if not preferences.bin_dir_ok():
                context['notice'] = BAD_BIN_DIR
            elif not data_dir_ok:
                context['notice'] = BAD_DATA_DIR
                context['subnotice'] = error
            else:
                context[
                    'header'] = "Accessing Bitcoin Core Wallet Interactively"
                context[
                    'notice'] = 'WARNING: Do not shut down your computer until Bitcoin-QT stops completely.'
                context['subnotice'] = SUBNOTICE

                if accessing_wallet():
                    log('already accessing wallet')

                else:
                    from blockchain_backup.bitcoin.access_wallet import AccessWalletTask

                    accessing_wallet_task = AccessWalletTask()
                    accessing_wallet_task.start()
                    log('access wallet started')

                # make sure the button doesn't appear any more
                bitcoin_utils.send_socketio_message('button', ' ')

            response = render(request,
                              'bitcoin/access_wallet.html',
                              context=context)

        return response