1# Copyright 2002 Ben Escoto
2#
3# This file is part of rdiff-backup.
4#
5# rdiff-backup is free software; you can redistribute it and/or modify
6# under the terms of the GNU General Public License as published by the
7# Free Software Foundation; either version 2 of the License, or (at your
8# option) any later version.
9#
10# rdiff-backup is distributed in the hope that it will be useful, but
11# WITHOUT ANY WARRANTY; without even the implied warranty of
12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13# General Public License for more details.
14#
15# You should have received a copy of the GNU General Public License
16# along with rdiff-backup; if not, write to the Free Software
17# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
18# 02110-1301, USA
19"""Hold a variety of constants usually set at initialization."""
20
21import re
22import os
23from . import log
24
25# The current version of rdiff-backup
26# Get it from package info or fall back to DEV version.
27try:
28    import pkg_resources
29    version = pkg_resources.get_distribution("rdiff-backup").version
30except BaseException:
31    version = "DEV"
32
33# If this is set, use this value in seconds as the current time
34# instead of reading it from the clock.
35current_time = None
36
37# This determines how many bytes to read at a time when copying
38blocksize = 131072
39
40# This is used by the BufferedRead class to determine how many
41# bytes to request from the underlying file per read().  Larger
42# values may save on connection overhead and latency.
43conn_bufsize = 393216
44
45# This is used in the CacheCollatedPostProcess and MiscIterToFile
46# classes.  The number represents the number of rpaths which may be
47# stuck in buffers when moving over a remote connection.
48pipeline_max_length = 500
49
50# True if script is running as a server
51server = None
52
53# uid and gid of the owner of the rdiff-backup process.  This can
54# vary depending on the connection.
55try:
56    process_uid = os.getuid()
57    process_gid = os.getgid()
58    process_groups = [process_gid] + os.getgroups()
59except AttributeError:
60    process_uid = 0
61    process_gid = 0
62    process_groups = [0]
63
64# If true, when copying attributes, also change target's uid/gid
65change_ownership = None
66
67# If true, change the permissions of unwriteable mirror files
68# (such as directories) so that they can be written, and then
69# change them back.  This defaults to 1 just in case the process
70# is not running as root (root doesn't need to change
71# permissions).
72change_mirror_perms = (process_uid != 0)
73
74# If true, try to reset the atimes of the source partition.
75preserve_atime = None
76
77# The following three attributes represent whether extended attributes
78# are supported.  If eas_active is true, then the current session
79# supports them.  If eas_write is true, then the extended attributes
80# should also be written to the destination side.  Finally, eas_conn
81# is relative to the current connection, and should be true iff that
82# particular connection supports extended attributes.
83eas_active = None
84eas_write = None
85eas_conn = None
86
87# The following settings are like the extended attribute settings, but
88# apply to access control lists instead.
89acls_active = None
90acls_write = None
91acls_conn = None
92
93# Like the above, but applies to support of Windows
94# access control lists.
95win_acls_active = None
96win_acls_write = None
97win_acls_conn = None
98
99# Like above two setting groups, but applies to support of Mac OS X
100# style resource forks.
101resource_forks_active = None
102resource_forks_write = None
103resource_forks_conn = None
104
105# Like the above, but applies to MacOS Carbon Finder creator/type info.
106# As of 1.0.2 this has defaulted to off because of bugs
107carbonfile_active = None
108carbonfile_write = None
109carbonfile_conn = None
110
111# This will be set as soon as the LocalConnection class loads
112local_connection = None
113
114# All connections should be added to the following list, so
115# further global changes can be propagated to the remote systems.
116# The first element should be Globals.local_connection.  For a
117# server, the second is the connection to the client.
118connections = []
119
120# Each process should have a connection number unique to the
121# session.  The client has connection number 0.
122connection_number = 0
123
124# Dictionary pairing connection numbers with connections.  Set in
125# SetConnections for all connections.
126connection_dict = {}
127
128# True if the script is the end that reads the source directory
129# for backups.  It is true for purely local sessions.
130isbackup_reader = None
131
132# Connection of the real backup reader (for which isbackup_reader
133# is true)
134backup_reader = None
135
136# True if the script is the end that writes to the increment and
137# mirror directories.  True for purely local sessions.
138isbackup_writer = None
139
140# Connection of the backup writer
141backup_writer = None
142
143# Connection of the client
144client_conn = None
145
146# When backing up, issource should be true on the reader and isdest on
147# the writer.  When restoring, issource should be true on the mirror
148# and isdest should be true on the target.
149issource = None
150isdest = None
151
152# This list is used by the set function below.  When a new
153# connection is created with init_connection, its Globals class
154# will match this one for all the variables mentioned in this
155# list.
156changed_settings = []
157
158# The RPath or QuotedRPath of the rdiff-backup-data directory.
159rbdir = None
160
161# chars_to_quote is a string whose characters should be quoted.  It
162# should be true if certain characters in filenames on the source side
163# should be escaped (see FilenameMapping for more info).
164chars_to_quote = None
165quoting_char = b';'
166
167# evaluate if DOS device names (AUX, PRN, CON, NUL, COM, LPT) should be quoted
168# or spaces at the end of file and directory names.
169# The default is based on the operating system type (nt or posix).
170escape_dos_devices = os.name == 'nt'
171escape_trailing_spaces = os.name == 'nt'
172
173# If true, the timestamps use the following format: "2008-09-01T04-49-04-07-00"
174# (instead of "2008-09-01T04:49:04-07:00"). This creates timestamps which
175# don't need to be escaped on Windows.
176use_compatible_timestamps = 0
177
178# Normally there shouldn't be any case of duplicate timestamp but it seems
179# we had the issue at some point in time, hence we need the flag to allow
180# users to clean up their repository. The default is to abort on such cases.
181
182allow_duplicate_timestamps = False
183
184# If true, emit output intended to be easily readable by a
185# computer.  False means output is intended for humans.
186parsable_output = None
187
188# If true, then hardlinks will be preserved to mirror and recorded
189# in the increments directory.  There is also a difference here
190# between None and 0.  When restoring, None or 1 means to preserve
191# hardlinks iff can find a hardlink dictionary.  0 means ignore
192# hardlink information regardless.
193preserve_hardlinks = 1
194
195# If this is false, then rdiff-backup will not compress any
196# increments.  Default is to compress based on regexp below.
197compression = 1
198
199# Increments based on files whose names match this
200# case-insensitive regular expression won't be compressed (applies
201# to .snapshots and .diffs).  The second below will be the
202# compiled version of the first.
203no_compression_regexp_string = (
204    b"(?i).*\\.(gz|z|bz|bz2|tgz|zip|zst|rpm|deb|"
205    b"jpg|jpeg|gif|png|jp2|mp3|mp4|ogg|ogv|oga|ogm|avi|wmv|mpeg|mpg|rm|mov|mkv|flac|shn|pgp|"
206    b"gpg|rz|lz4|lzh|lzo|zoo|lharc|rar|arj|asc|vob|mdf)$")
207no_compression_regexp = None
208
209# If true, filelists and directory statistics will be split on
210# nulls instead of newlines.
211null_separator = None
212
213# Determines whether or not ssh will be run with the -C switch
214ssh_compression = 1
215
216# If true, print statistics after successful backup
217print_statistics = None
218
219# Controls whether file_statistics file is written in
220# rdiff-backup-data dir.  These can sometimes take up a lot of space.
221file_statistics = 1
222
223# On the writer connection, the following will be set to the mirror
224# Select iterator.
225select_mirror = None
226
227# On the backup writer connection, holds the root incrementing branch
228# object.  Access is provided to increment error counts.
229ITRB = None
230
231# security_level has 4 values and controls which requests from remote
232# systems will be honored.  "all" means anything goes. "read-only"
233# means that the requests must not write to disk.  "update-only" means
234# that requests shouldn't destructively update the disk (but normal
235# incremental updates are OK).  "minimal" means only listen to a few
236# basic requests.
237security_level = "all"
238
239# If this is set, it indicates that the remote connection should only
240# deal with paths inside of restrict_path.
241restrict_path = None
242
243# If set, a file will be marked as changed if its inode changes.  See
244# the man page under --no-compare-inode for more information.
245compare_inode = 1
246
247# If set, directories can be fsync'd just like normal files, to
248# guarantee that any changes have been comitted to disk.
249fsync_directories = None
250
251# If set, exit with error instead of dropping ACLs or ACL entries.
252never_drop_acls = None
253
254# Apply this mask to permissions before chmoding.  (Set to 0777 to
255# prevent highbit permissions on systems which don't support them.)
256permission_mask = 0o7777
257
258# If true, symlinks permissions are affected by the process umask, and
259# we should change the umask when creating them in order to preserve
260# the original permissions
261symlink_perms = None
262
263# If set, the path that should be used instead of the default Python
264# tempfile.tempdir value on remote connections
265remote_tempdir = None
266
267# Fsync everything by default. Use --no-fsync only if you really know what you are doing
268# Not having the data written to disk may render your backup unusable in case of FS failure.
269# Using --no-fsync disables only fsync of files during backup and sync() system call upon backup finish
270# and pre-regress
271do_fsync = True
272
273
274def get(name):
275    """Return the value of something in this module"""
276    return globals()[name]
277
278
279def is_not_None(name):
280    """Returns true if value is not None"""
281    return globals()[name] is not None
282
283
284def set(name, val):
285    """Set the value of something in this module
286
287    Use this instead of writing the values directly if the setting
288    matters to remote sides.  This function updates the
289    changed_settings list, so other connections know to copy the
290    changes.
291
292    """
293    changed_settings.append(name)
294    globals()[name] = val
295
296
297def set_local(name, val):
298    """Like set above, but only set current connection"""
299    globals()[name] = val
300
301
302def set_integer(name, val):
303    """Like set, but make sure val is an integer"""
304    try:
305        intval = int(val)
306    except ValueError:
307        log.Log.FatalError("Variable %s must be set to an integer -\n"
308                           "received %s instead." % (name, val))
309    set(name, intval)
310
311
312def set_float(name, val, min=None, max=None, inclusive=1):
313    """Like set, but make sure val is float within given bounds"""
314
315    def error():
316        s = "Variable %s must be set to a float" % (name, )
317        if min is not None and max is not None:
318            s += " between %s and %s " % (min, max)
319            if inclusive:
320                s += "inclusive"
321            else:
322                s += "not inclusive"
323        elif min is not None or max is not None:
324            if inclusive:
325                inclusive_string = "or equal to "
326            else:
327                inclusive_string = ""
328            if min is not None:
329                s += " greater than %s%s" % (inclusive_string, min)
330            else:
331                s += " less than %s%s" % (inclusive_string, max)
332        log.Log.FatalError(s)
333
334    try:
335        f = float(val)
336    except ValueError:
337        error()
338    if min is not None:
339        if inclusive and f < min:
340            error()
341        elif not inclusive and f <= min:
342            error()
343    if max is not None:
344        if inclusive and f > max:
345            error()
346        elif not inclusive and f >= max:
347            error()
348    set(name, f)
349
350
351def get_dict_val(name, key):
352    """Return val from dictionary in this class"""
353    return globals()[name][key]
354
355
356def set_dict_val(name, key, val):
357    """Set value for dictionary in this class"""
358    globals()[name][key] = val
359
360
361def postset_regexp(name, re_string, flags=None):
362    """Compile re_string on all existing connections, set to name"""
363    for conn in connections:
364        conn.Globals.postset_regexp_local(name, re_string, flags)
365
366
367def postset_regexp_local(name, re_string, flags):
368    """Set name to compiled re_string locally"""
369    if flags:
370        globals()[name] = re.compile(re_string, flags)
371    else:
372        globals()[name] = re.compile(re_string)
373