This file is indexed.

/usr/lib/python2.7/dist-packages/barman/hooks.py is in barman 2.3-2.

This file is owned by root:root, with mode 0o644.

The actual contents of the file can be viewed below.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
# Copyright (C) 2011-2017 2ndQuadrant Limited
#
# This file is part of Barman.
#
# Barman is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Barman is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Barman.  If not, see <http://www.gnu.org/licenses/>.

"""
This module contains the logic to run hook scripts
"""

import logging
import time

from barman import version
from barman.command_wrappers import Command
from barman.exceptions import AbortedRetryHookScript, UnknownBackupIdException

_logger = logging.getLogger(__name__)


class HookScriptRunner(object):
    def __init__(self, backup_manager, name, phase=None, error=None,
                 retry=False, **extra_env):
        """
        Execute a hook script managing its environment
        """
        self.backup_manager = backup_manager
        self.name = name
        self.extra_env = extra_env
        self.phase = phase
        self.error = error
        self.retry = retry

        self.environment = None
        self.exit_status = None
        self.exception = None
        self.script = None

        self.reset()

    def reset(self):
        """
        Reset the status of the class.
        """
        self.environment = dict(self.extra_env)
        config_file = self.backup_manager.config.config.config_file
        self.environment.update({
            'BARMAN_VERSION': version.__version__,
            'BARMAN_SERVER': self.backup_manager.config.name,
            'BARMAN_CONFIGURATION': config_file,
            'BARMAN_HOOK': self.name,
            'BARMAN_RETRY': str(1 if self.retry else 0),
        })
        if self.error:
            self.environment['BARMAN_ERROR'] = str(self.error)
        if self.phase:
            self.environment['BARMAN_PHASE'] = self.phase
            script_config_name = "%s_%s" % (self.phase, self.name)
        else:
            script_config_name = self.name
        self.script = getattr(self.backup_manager.config, script_config_name,
                              None)
        self.exit_status = None
        self.exception = None

    def env_from_backup_info(self, backup_info):
        """
        Prepare the environment for executing a script

        :param BackupInfo backup_info: the backup metadata
        """
        try:
            previous_backup = self.backup_manager.get_previous_backup(
                backup_info.backup_id)
            if previous_backup:
                previous_backup_id = previous_backup.backup_id
            else:
                previous_backup_id = ''
        except UnknownBackupIdException:
            previous_backup_id = ''
        self.environment.update({
            'BARMAN_BACKUP_DIR': backup_info.get_basebackup_directory(),
            'BARMAN_BACKUP_ID': backup_info.backup_id,
            'BARMAN_PREVIOUS_ID': previous_backup_id,
            'BARMAN_STATUS': backup_info.status,
            'BARMAN_ERROR': backup_info.error or '',
        })

    def env_from_wal_info(self, wal_info, full_path=None, error=None):
        """
        Prepare the environment for executing a script

        :param WalFileInfo wal_info: the backup metadata
        :param str full_path: override wal_info.fullpath() result
        :param str|Exception error: An error message in case of failure
        """
        self.environment.update({
            'BARMAN_SEGMENT': wal_info.name,
            'BARMAN_FILE': str(full_path if full_path is not None else
                               wal_info.fullpath(self.backup_manager.server)),
            'BARMAN_SIZE': str(wal_info.size),
            'BARMAN_TIMESTAMP': str(wal_info.time),
            'BARMAN_COMPRESSION': wal_info.compression or '',
            'BARMAN_ERROR': str(error or '')
        })

    def run(self):
        """
        Run a a hook script if configured.
        This method must never throw any exception
        """
        # noinspection PyBroadException
        try:
            if self.script:
                _logger.debug("Attempt to run %s: %s", self.name, self.script)
                cmd = Command(
                    self.script,
                    env_append=self.environment,
                    path=self.backup_manager.server.path,
                    shell=True, check=False)
                self.exit_status = cmd()
                if self.exit_status != 0:
                    details = "%s returned %d\n" \
                              "Output details:\n" \
                              % (self.script, self.exit_status)
                    details += cmd.out
                    details += cmd.err
                    _logger.warning(details)
                else:
                    _logger.debug("%s returned %d",
                                  self.script,
                                  self.exit_status)
                return self.exit_status
        except Exception as e:
            _logger.exception('Exception running %s', self.name)
            self.exception = e
            return None


class RetryHookScriptRunner(HookScriptRunner):

    """
    A 'retry' hook script is a special kind of hook script that Barman
    tries to run indefinitely until it either returns a SUCCESS or
    ABORT exit code.
    Retry hook scripts are executed immediately before (pre) and after (post)
    the command execution. Standard hook scripts are executed immediately
    before (pre) and after (post) the retry hook scripts.
    """

    # Failed attempts before sleeping for NAP_TIME seconds
    ATTEMPTS_BEFORE_NAP = 5
    # Short break after a failure (in seconds)
    BREAK_TIME = 3
    # Long break (nap, in seconds) after ATTEMPTS_BEFORE_NAP failures
    NAP_TIME = 60
    # ABORT (and STOP) exit code
    EXIT_ABORT_STOP = 63
    # ABORT (and CONTINUE) exit code
    EXIT_ABORT_CONTINUE = 62
    # SUCCESS exit code
    EXIT_SUCCESS = 0

    def __init__(self, backup_manager, name, phase=None, error=None,
                 **extra_env):
        super(RetryHookScriptRunner, self).__init__(
            backup_manager, name, phase, error, retry=True, **extra_env)

    def run(self):
        """
        Run a a 'retry' hook script, if required by configuration.

        Barman will retry to run the script indefinitely until it returns
        a EXIT_SUCCESS, or an EXIT_ABORT_CONTINUE, or an EXIT_ABORT_STOP code.
        There are BREAK_TIME seconds of sleep between every try.
        Every ATTEMPTS_BEFORE_NAP failures, Barman will sleep
        for NAP_TIME seconds.
        """
        # If there is no script, exit
        if self.script is not None:
            # Keep track of the number of attempts
            attempts = 1
            while True:
                # Run the script using the standard hook method (inherited)
                super(RetryHookScriptRunner, self).run()

                # Run the script until it returns EXIT_ABORT_CONTINUE,
                # or an EXIT_ABORT_STOP, or EXIT_SUCCESS
                if self.exit_status in (self.EXIT_ABORT_CONTINUE,
                                        self.EXIT_ABORT_STOP,
                                        self.EXIT_SUCCESS):
                    break

                # Check for the number of attempts
                if attempts <= self.ATTEMPTS_BEFORE_NAP:
                    attempts += 1
                    # Take a short break
                    _logger.debug("Retry again in %d seconds", self.BREAK_TIME)
                    time.sleep(self.BREAK_TIME)
                else:
                    # Reset the attempt number and take a longer nap
                    _logger.debug("Reached %d failures. Take a nap "
                                  "then retry again in %d seconds",
                                  self.ATTEMPTS_BEFORE_NAP,
                                  self.NAP_TIME)
                    attempts = 1
                    time.sleep(self.NAP_TIME)

            # Outside the loop check for the exit code.
            if self.exit_status == self.EXIT_ABORT_CONTINUE:
                # Warn the user if the script exited with EXIT_ABORT_CONTINUE
                # Notify EXIT_ABORT_CONTINUE exit status because success and
                # failures are already managed in the superclass run method
                _logger.warning("%s was aborted (got exit status %d, "
                                "Barman resumes)",
                                self.script,
                                self.exit_status)
            elif self.exit_status == self.EXIT_ABORT_STOP:
                # Log the error and raise AbortedRetryHookScript exception
                _logger.error("%s was aborted (got exit status %d, "
                              "Barman requested to stop)",
                              self.script,
                              self.exit_status)
                raise AbortedRetryHookScript(self)

            return self.exit_status