diff options
author | Jelmer Vernooij <jelmer@debian.org> | 2015-09-19 02:56:39 +0000 |
---|---|---|
committer | Jelmer Vernooij <jelmer@debian.org> | 2015-09-19 02:56:39 +0000 |
commit | 72c40d6046e26a0ea8d1463d8cfadcd5f8c0a022 (patch) | |
tree | ab5a7212cc05abeeca83b45339ad401f6c091e73 | |
parent | 7d7a23bf2a1f1820101d9eec51f16bb2fd78d1a0 (diff) | |
parent | 94f15f4ac5cc4986d7be05c6d428c3cb2e824381 (diff) |
Merge tag 'upstream/1.3.7'
Upstream version 1.3.7
# gpg: Signature made Sat 19 Sep 2015 02:56:37 UTC using RSA key ID 5E63D2DA
# gpg: Good signature from "Jelmer Vernooij <jelmer@jelmer.uk>"
# gpg: aka "Jelmer Vernooij <jelmer@samba.org>"
# gpg: aka "Jelmer Vernooij <jelmer@apache.org>"
# gpg: aka "Jelmer Vernooij <jelmer@debian.org>"
# gpg: aka "Jelmer Vernooij <jelmer@ubuntu.com>"
# gpg: aka "Jelmer Vernooij <jelmer@vernstok.nl>"
# gpg: aka "Jelmer Vernooij <jelmer@jelmer.co.uk>"
# gpg: aka "Jelmer Vernooij <jelmer@openchange.org>"
# gpg: aka "Jelmer Vernooij <jrvernooij@tigris.org>"
27 files changed, 1357 insertions, 331 deletions
diff --git a/ABI/tdb-1.3.6.sigs b/ABI/tdb-1.3.6.sigs new file mode 100644 index 0000000..2545c99 --- /dev/null +++ b/ABI/tdb-1.3.6.sigs @@ -0,0 +1,69 @@ +tdb_add_flags: void (struct tdb_context *, unsigned int) +tdb_append: int (struct tdb_context *, TDB_DATA, TDB_DATA) +tdb_chainlock: int (struct tdb_context *, TDB_DATA) +tdb_chainlock_mark: int (struct tdb_context *, TDB_DATA) +tdb_chainlock_nonblock: int (struct tdb_context *, TDB_DATA) +tdb_chainlock_read: int (struct tdb_context *, TDB_DATA) +tdb_chainlock_read_nonblock: int (struct tdb_context *, TDB_DATA) +tdb_chainlock_unmark: int (struct tdb_context *, TDB_DATA) +tdb_chainunlock: int (struct tdb_context *, TDB_DATA) +tdb_chainunlock_read: int (struct tdb_context *, TDB_DATA) +tdb_check: int (struct tdb_context *, int (*)(TDB_DATA, TDB_DATA, void *), void *) +tdb_close: int (struct tdb_context *) +tdb_delete: int (struct tdb_context *, TDB_DATA) +tdb_dump_all: void (struct tdb_context *) +tdb_enable_seqnum: void (struct tdb_context *) +tdb_error: enum TDB_ERROR (struct tdb_context *) +tdb_errorstr: const char *(struct tdb_context *) +tdb_exists: int (struct tdb_context *, TDB_DATA) +tdb_fd: int (struct tdb_context *) +tdb_fetch: TDB_DATA (struct tdb_context *, TDB_DATA) +tdb_firstkey: TDB_DATA (struct tdb_context *) +tdb_freelist_size: int (struct tdb_context *) +tdb_get_flags: int (struct tdb_context *) +tdb_get_logging_private: void *(struct tdb_context *) +tdb_get_seqnum: int (struct tdb_context *) +tdb_hash_size: int (struct tdb_context *) +tdb_increment_seqnum_nonblock: void (struct tdb_context *) +tdb_jenkins_hash: unsigned int (TDB_DATA *) +tdb_lock_nonblock: int (struct tdb_context *, int, int) +tdb_lockall: int (struct tdb_context *) +tdb_lockall_mark: int (struct tdb_context *) +tdb_lockall_nonblock: int (struct tdb_context *) +tdb_lockall_read: int (struct tdb_context *) +tdb_lockall_read_nonblock: int (struct tdb_context *) +tdb_lockall_unmark: int (struct tdb_context *) +tdb_log_fn: tdb_log_func (struct tdb_context *) +tdb_map_size: size_t (struct tdb_context *) +tdb_name: const char *(struct tdb_context *) +tdb_nextkey: TDB_DATA (struct tdb_context *, TDB_DATA) +tdb_null: dptr = 0xXXXX, dsize = 0 +tdb_open: struct tdb_context *(const char *, int, int, int, mode_t) +tdb_open_ex: struct tdb_context *(const char *, int, int, int, mode_t, const struct tdb_logging_context *, tdb_hash_func) +tdb_parse_record: int (struct tdb_context *, TDB_DATA, int (*)(TDB_DATA, TDB_DATA, void *), void *) +tdb_printfreelist: int (struct tdb_context *) +tdb_remove_flags: void (struct tdb_context *, unsigned int) +tdb_reopen: int (struct tdb_context *) +tdb_reopen_all: int (int) +tdb_repack: int (struct tdb_context *) +tdb_rescue: int (struct tdb_context *, void (*)(TDB_DATA, TDB_DATA, void *), void *) +tdb_runtime_check_for_robust_mutexes: bool (void) +tdb_set_logging_function: void (struct tdb_context *, const struct tdb_logging_context *) +tdb_set_max_dead: void (struct tdb_context *, int) +tdb_setalarm_sigptr: void (struct tdb_context *, volatile sig_atomic_t *) +tdb_store: int (struct tdb_context *, TDB_DATA, TDB_DATA, int) +tdb_summary: char *(struct tdb_context *) +tdb_transaction_cancel: int (struct tdb_context *) +tdb_transaction_commit: int (struct tdb_context *) +tdb_transaction_prepare_commit: int (struct tdb_context *) +tdb_transaction_start: int (struct tdb_context *) +tdb_transaction_start_nonblock: int (struct tdb_context *) +tdb_transaction_write_lock_mark: int (struct tdb_context *) +tdb_transaction_write_lock_unmark: int (struct tdb_context *) +tdb_traverse: int (struct tdb_context *, tdb_traverse_func, void *) +tdb_traverse_read: int (struct tdb_context *, tdb_traverse_func, void *) +tdb_unlock: int (struct tdb_context *, int, int) +tdb_unlockall: int (struct tdb_context *) +tdb_unlockall_read: int (struct tdb_context *) +tdb_validate_freelist: int (struct tdb_context *, int *) +tdb_wipe_all: int (struct tdb_context *) diff --git a/ABI/tdb-1.3.7.sigs b/ABI/tdb-1.3.7.sigs new file mode 100644 index 0000000..2545c99 --- /dev/null +++ b/ABI/tdb-1.3.7.sigs @@ -0,0 +1,69 @@ +tdb_add_flags: void (struct tdb_context *, unsigned int) +tdb_append: int (struct tdb_context *, TDB_DATA, TDB_DATA) +tdb_chainlock: int (struct tdb_context *, TDB_DATA) +tdb_chainlock_mark: int (struct tdb_context *, TDB_DATA) +tdb_chainlock_nonblock: int (struct tdb_context *, TDB_DATA) +tdb_chainlock_read: int (struct tdb_context *, TDB_DATA) +tdb_chainlock_read_nonblock: int (struct tdb_context *, TDB_DATA) +tdb_chainlock_unmark: int (struct tdb_context *, TDB_DATA) +tdb_chainunlock: int (struct tdb_context *, TDB_DATA) +tdb_chainunlock_read: int (struct tdb_context *, TDB_DATA) +tdb_check: int (struct tdb_context *, int (*)(TDB_DATA, TDB_DATA, void *), void *) +tdb_close: int (struct tdb_context *) +tdb_delete: int (struct tdb_context *, TDB_DATA) +tdb_dump_all: void (struct tdb_context *) +tdb_enable_seqnum: void (struct tdb_context *) +tdb_error: enum TDB_ERROR (struct tdb_context *) +tdb_errorstr: const char *(struct tdb_context *) +tdb_exists: int (struct tdb_context *, TDB_DATA) +tdb_fd: int (struct tdb_context *) +tdb_fetch: TDB_DATA (struct tdb_context *, TDB_DATA) +tdb_firstkey: TDB_DATA (struct tdb_context *) +tdb_freelist_size: int (struct tdb_context *) +tdb_get_flags: int (struct tdb_context *) +tdb_get_logging_private: void *(struct tdb_context *) +tdb_get_seqnum: int (struct tdb_context *) +tdb_hash_size: int (struct tdb_context *) +tdb_increment_seqnum_nonblock: void (struct tdb_context *) +tdb_jenkins_hash: unsigned int (TDB_DATA *) +tdb_lock_nonblock: int (struct tdb_context *, int, int) +tdb_lockall: int (struct tdb_context *) +tdb_lockall_mark: int (struct tdb_context *) +tdb_lockall_nonblock: int (struct tdb_context *) +tdb_lockall_read: int (struct tdb_context *) +tdb_lockall_read_nonblock: int (struct tdb_context *) +tdb_lockall_unmark: int (struct tdb_context *) +tdb_log_fn: tdb_log_func (struct tdb_context *) +tdb_map_size: size_t (struct tdb_context *) +tdb_name: const char *(struct tdb_context *) +tdb_nextkey: TDB_DATA (struct tdb_context *, TDB_DATA) +tdb_null: dptr = 0xXXXX, dsize = 0 +tdb_open: struct tdb_context *(const char *, int, int, int, mode_t) +tdb_open_ex: struct tdb_context *(const char *, int, int, int, mode_t, const struct tdb_logging_context *, tdb_hash_func) +tdb_parse_record: int (struct tdb_context *, TDB_DATA, int (*)(TDB_DATA, TDB_DATA, void *), void *) +tdb_printfreelist: int (struct tdb_context *) +tdb_remove_flags: void (struct tdb_context *, unsigned int) +tdb_reopen: int (struct tdb_context *) +tdb_reopen_all: int (int) +tdb_repack: int (struct tdb_context *) +tdb_rescue: int (struct tdb_context *, void (*)(TDB_DATA, TDB_DATA, void *), void *) +tdb_runtime_check_for_robust_mutexes: bool (void) +tdb_set_logging_function: void (struct tdb_context *, const struct tdb_logging_context *) +tdb_set_max_dead: void (struct tdb_context *, int) +tdb_setalarm_sigptr: void (struct tdb_context *, volatile sig_atomic_t *) +tdb_store: int (struct tdb_context *, TDB_DATA, TDB_DATA, int) +tdb_summary: char *(struct tdb_context *) +tdb_transaction_cancel: int (struct tdb_context *) +tdb_transaction_commit: int (struct tdb_context *) +tdb_transaction_prepare_commit: int (struct tdb_context *) +tdb_transaction_start: int (struct tdb_context *) +tdb_transaction_start_nonblock: int (struct tdb_context *) +tdb_transaction_write_lock_mark: int (struct tdb_context *) +tdb_transaction_write_lock_unmark: int (struct tdb_context *) +tdb_traverse: int (struct tdb_context *, tdb_traverse_func, void *) +tdb_traverse_read: int (struct tdb_context *, tdb_traverse_func, void *) +tdb_unlock: int (struct tdb_context *, int, int) +tdb_unlockall: int (struct tdb_context *) +tdb_unlockall_read: int (struct tdb_context *) +tdb_validate_freelist: int (struct tdb_context *, int *) +tdb_wipe_all: int (struct tdb_context *) diff --git a/_tdb_text.py b/_tdb_text.py new file mode 100644 index 0000000..c823bf8 --- /dev/null +++ b/_tdb_text.py @@ -0,0 +1,138 @@ +# Text wrapper for tdb bindings +# +# Copyright (C) 2015 Petr Viktorin <pviktori@redhat.com> +# Published under the GNU LGPLv3 or later + +import sys +import functools + +import tdb + + +class TdbTextWrapper(object): + """Text interface for a TDB file""" + + def __init__(self, tdb): + self._tdb = tdb + + @property + def raw(self): + return self._tdb + + def get(self, key): + key = key.encode('utf-8') + result = self._tdb.get(key) + if result is not None: + return result.decode('utf-8') + + def append(self, key, value): + key = key.encode('utf-8') + value = value.encode('utf-8') + self._tdb.append(key, value) + + def firstkey(self): + result = self._tdb.firstkey() + if result: + return result.decode('utf-8') + + def nextkey(self, key): + key = key.encode('utf-8') + result = self._tdb.nextkey(key) + if result is not None: + return result.decode('utf-8') + + def delete(self, key): + key = key.encode('utf-8') + self._tdb.delete(key) + + def store(self, key, value): + key = key.encode('utf-8') + value = value.encode('utf-8') + self._tdb.store(key, value) + + def __iter__(self): + for key in iter(self._tdb): + yield key.decode('utf-8') + + def __getitem__(self, key): + key = key.encode('utf-8') + result = self._tdb[key] + return result.decode('utf-8') + + def __contains__(self, key): + key = key.encode('utf-8') + return key in self._tdb + + def __repr__(self): + return '<TdbTextWrapper for %r>' % self._tdb + + def __setitem__(self, key, value): + key = key.encode('utf-8') + value = value.encode('utf-8') + self._tdb[key] = value + + def __delitem__(self, key): + key = key.encode('utf-8') + del self._tdb[key] + + if sys.version_info > (3, 0): + keys = __iter__ + else: + iterkeys = __iter__ + has_key = __contains__ + + +## Add wrappers for functions and getters that don't deal with text + +def _add_wrapper(name): + orig = getattr(tdb.Tdb, name) + + def wrapper(self, *args, **kwargs): + return orig(self._tdb, *args, **kwargs) + wrapper.__name__ = orig.__name__ + wrapper.__doc__ = orig.__doc__ + + setattr(TdbTextWrapper, name, wrapper) + +for name in ("transaction_cancel", + "transaction_commit", + "transaction_prepare_commit", + "transaction_start", + "reopen", + "lock_all", + "unlock_all", + "read_lock_all", + "read_unlock_all", + "close", + "add_flags", + "remove_flags", + "clear", + "repack", + "enable_seqnum", + "increment_seqnum_nonblock", + ): + _add_wrapper(name) + + +def _add_getter(name): + orig = getattr(tdb.Tdb, name) + doc = orig.__doc__ + + def getter(self): + return getattr(self._tdb, name) + + def setter(self, value): + return setattr(self._tdb, name, value) + + setattr(TdbTextWrapper, name, property(getter, setter, doc=doc)) + +for name in ("hash_size", + "map_size", + "freelist_size", + "flags", + "max_dead", + "filename", + "seqnum", + "text", + ): + _add_getter(name) diff --git a/buildtools/examples/run_on_target.py b/buildtools/examples/run_on_target.py new file mode 100755 index 0000000..8322759 --- /dev/null +++ b/buildtools/examples/run_on_target.py @@ -0,0 +1,148 @@ +#!/usr/bin/env python + +# +# Sample run-on-target script +# This is a script that can be used as cross-execute parameter to samba +# configuration process, running the command on a remote target for which +# the cross-compiled configure test was compiled. +# +# To use: +# ./configure \ +# --cross-compile \ +# '--cross-execute=./buildtools/example/run_on_target.py --host=<host>' +# +# A more elaborate example: +# ./configure \ +# --cross-compile \ +# '--cross-execute=./buildtools/example/run_on_target.py --host=<host> --user=<user> "--ssh=ssh -i <some key file>" --destdir=/path/to/dir' +# +# Typically this is to be used also with --cross-answers, so that the +# cross answers file gets built and further builds can be made without +# the help of a remote target. +# +# The following assumptions are made: +# 1. rsync is available on build machine and target machine +# 2. A running ssh service on target machine with password-less shell login +# 3. A directory writable by the password-less login user +# 4. The tests on the target can run and provide reliable results +# from the login account's home directory. This is significant +# for example in locking tests which +# create files in the current directory. As a workaround to this +# assumption, the TESTDIR environment variable can be set on the target +# (using ssh command line or server config) and the tests shall +# chdir to that directory. +# + +import sys +import os +import subprocess +from optparse import OptionParser + +# those are defaults, but can be overidden using command line +SSH = 'ssh' +USER = None +HOST = 'localhost' + + +def xfer_files(ssh, srcdir, host, user, targ_destdir): + """Transfer executable files to target + + Use rsync to copy the directory containing program to run + INTO a destination directory on the target. An exact copy + of the source directory is created on the target machine, + possibly deleting files on the target machine which do not + exist on the source directory. + + The idea is that the test may include files in addition to + the compiled binary, and all of those files reside alongside + the binary in a source directory. + + For example, if the test to run is /foo/bar/test and the + destination directory on the target is /tbaz, then /tbaz/bar + on the target shall be an exact copy of /foo/bar on the source, + including deletion of files inside /tbaz/bar which do not exist + on the source. + """ + + userhost = host + if user: + userhost = '%s@%s' % (user, host) + + cmd = 'rsync --verbose -rl --ignore-times --delete -e "%s" %s %s:%s/' % \ + (ssh, srcdir, userhost, targ_destdir) + p = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + (out, err) = p.communicate() + if p.returncode != 0: + raise Exception('failed syncing files\n stdout:\n%s\nstderr:%s\n' + % (out, err)) + + +def exec_remote(ssh, host, user, destdir, targdir, prog, args): + """Run a test on the target + + Using password-less ssh, run the compiled binary on the target. + + An assumption is that there's no need to cd into the target dir, + same as there's no need to do it on a native build. + """ + userhost = host + if user: + userhost = '%s@%s' % (user, host) + + cmd = '%s %s %s/%s/%s' % (ssh, userhost, destdir, targdir, prog) + if args: + cmd = cmd + ' ' + ' '.join(args) + p = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + (out, err) = p.communicate() + return (p.returncode, out) + + +def main(argv): + usage = "usage: %prog [options] <prog> [args]" + parser = OptionParser(usage) + + parser.add_option('--ssh', help="SSH client and additional flags", + default=SSH) + parser.add_option('--host', help="target host name or IP address", + default=HOST) + parser.add_option('--user', help="login user on target", + default=USER) + parser.add_option('--destdir', help="work directory on target", + default='~') + + (options, args) = parser.parse_args(argv) + if len(args) < 1: + parser.error("please supply test program to run") + + progpath = args[0] + + # assume that a test that was not compiled fails (e.g. getconf) + if progpath[0] != '/': + return (1, "") + + progdir = os.path.dirname(progpath) + prog = os.path.basename(progpath) + targ_progdir = os.path.basename(progdir) + + xfer_files( + options.ssh, + progdir, + options.host, + options.user, + options.destdir) + + (rc, out) = exec_remote(options.ssh, + options.host, + options.user, + options.destdir, + targ_progdir, + prog, args[1:]) + return (rc, out) + + +if __name__ == '__main__': + (rc, out) = main(sys.argv[1:]) + sys.stdout.write(out) + sys.exit(rc) diff --git a/buildtools/wafsamba/samba_autoconf.py b/buildtools/wafsamba/samba_autoconf.py index 4f646fd..c5f132c 100644 --- a/buildtools/wafsamba/samba_autoconf.py +++ b/buildtools/wafsamba/samba_autoconf.py @@ -229,7 +229,18 @@ def CHECK_DECLS(conf, vars, reverse=False, headers=None, always=False): headers=headers, msg='Checking for declaration of %s' % v, always=always): - ret = False + if not CHECK_CODE(conf, + ''' + return (int)%s; + ''' % (v), + execute=False, + link=False, + msg='Checking for declaration of %s (as enum)' % v, + local_include=False, + headers=headers, + define=define, + always=always): + ret = False return ret diff --git a/buildtools/wafsamba/samba_bundled.py b/buildtools/wafsamba/samba_bundled.py index 515590f..c8bfcd2 100644 --- a/buildtools/wafsamba/samba_bundled.py +++ b/buildtools/wafsamba/samba_bundled.py @@ -190,7 +190,7 @@ def CHECK_BUNDLED_SYSTEM(conf, libname, minversion='0.0.0', pkg = libname # try pkgconfig first - if (conf.check_cfg(package=pkg, + if (conf.CHECK_CFG(package=pkg, args='"%s >= %s" --cflags --libs' % (pkg, minversion), msg=msg, uselib_store=uselib_store) and check_functions_headers_code()): diff --git a/buildtools/wafsamba/samba_cross.py b/buildtools/wafsamba/samba_cross.py index 3838e34..ed3af1e 100644 --- a/buildtools/wafsamba/samba_cross.py +++ b/buildtools/wafsamba/samba_cross.py @@ -2,11 +2,12 @@ import Utils, Logs, sys, os, Options, re from Configure import conf +import shlex real_Popen = None ANSWER_UNKNOWN = (254, "") -ANSWER_FAIL = (255, "") +ANSWER_NO = (1, "") ANSWER_OK = (0, "") cross_answers_incomplete = False @@ -19,15 +20,27 @@ def add_answer(ca_file, msg, answer): except: Logs.error("Unable to open cross-answers file %s" % ca_file) sys.exit(1) + (retcode, retstring) = answer + # if retstring is more than one line then we probably + # don't care about its actual content (the tests should + # yield one-line output in order to comply with the cross-answer + # format) + retstring = retstring.strip() + if len(retstring.split('\n')) > 1: + retstring = '' + answer = (retcode, retstring) + if answer == ANSWER_OK: f.write('%s: OK\n' % msg) elif answer == ANSWER_UNKNOWN: f.write('%s: UNKNOWN\n' % msg) - elif answer == ANSWER_FAIL: - f.write('%s: FAIL\n' % msg) + elif answer == ANSWER_NO: + f.write('%s: NO\n' % msg) else: - (retcode, retstring) = answer - f.write('%s: (%d, "%s")' % (msg, retcode, retstring)) + if retcode == 0: + f.write('%s: "%s"\n' % (msg, retstring)) + else: + f.write('%s: (%d, "%s")\n' % (msg, retcode, retstring)) f.close() @@ -36,14 +49,13 @@ def cross_answer(ca_file, msg): try: f = open(ca_file, 'r') except: - add_answer(ca_file, msg, ANSWER_UNKNOWN) return ANSWER_UNKNOWN for line in f: line = line.strip() if line == '' or line[0] == '#': continue if line.find(':') != -1: - a = line.split(':') + a = line.split(':', 1) thismsg = a[0].strip() if thismsg != msg: continue @@ -56,10 +68,12 @@ def cross_answer(ca_file, msg): return ANSWER_UNKNOWN elif ans == "FAIL" or ans == "NO": f.close() - return ANSWER_FAIL + return ANSWER_NO elif ans[0] == '"': + f.close() return (0, ans.strip('"')) elif ans[0] == "'": + f.close() return (0, ans.strip("'")) else: m = re.match('\(\s*(-?\d+)\s*,\s*\"(.*)\"\s*\)', ans) @@ -69,7 +83,6 @@ def cross_answer(ca_file, msg): else: raise Utils.WafError("Bad answer format '%s' in %s" % (line, ca_file)) f.close() - add_answer(ca_file, msg, ANSWER_UNKNOWN) return ANSWER_UNKNOWN @@ -77,24 +90,47 @@ class cross_Popen(Utils.pproc.Popen): '''cross-compilation wrapper for Popen''' def __init__(*k, **kw): (obj, args) = k - - if '--cross-execute' in args: - # when --cross-execute is set, then change the arguments - # to use the cross emulator - i = args.index('--cross-execute') - newargs = args[i+1].split() - newargs.extend(args[0:i]) - args = newargs - elif '--cross-answers' in args: + use_answers = False + ans = ANSWER_UNKNOWN + + # Three possibilities: + # 1. Only cross-answers - try the cross-answers file, and if + # there's no corresponding answer, add to the file and mark + # the configure process as unfinished. + # 2. Only cross-execute - get the answer from cross-execute + # 3. Both - try the cross-answers file, and if there is no + # corresponding answer - use cross-execute to get an answer, + # and add that answer to the file. + if '--cross-answers' in args: # when --cross-answers is set, then change the arguments # to use the cross answers if available + use_answers = True i = args.index('--cross-answers') ca_file = args[i+1] msg = args[i+2] ans = cross_answer(ca_file, msg) + + if '--cross-execute' in args and ans == ANSWER_UNKNOWN: + # when --cross-execute is set, then change the arguments + # to use the cross emulator + i = args.index('--cross-execute') + newargs = shlex.split(args[i+1]) + newargs.extend(args[0:i]) + if use_answers: + p = real_Popen(newargs, + stdout=Utils.pproc.PIPE, + stderr=Utils.pproc.PIPE) + ce_out, ce_err = p.communicate() + ans = (p.returncode, ce_out) + add_answer(ca_file, msg, ans) + else: + args = newargs + + if use_answers: if ans == ANSWER_UNKNOWN: global cross_answers_incomplete cross_answers_incomplete = True + add_answer(ca_file, msg, ans) (retcode, retstring) = ans args = ['/bin/sh', '-c', "echo -n '%s'; exit %d" % (retstring, retcode)] real_Popen.__init__(*(obj, args), **kw) @@ -115,7 +151,8 @@ def SAMBA_CROSS_ARGS(conf, msg=None): if conf.env.CROSS_EXECUTE: ret.extend(['--cross-execute', conf.env.CROSS_EXECUTE]) - elif conf.env.CROSS_ANSWERS: + + if conf.env.CROSS_ANSWERS: if msg is None: raise Utils.WafError("Cannot have NULL msg in cross-answers") ret.extend(['--cross-answers', os.path.join(Options.launch_dir, conf.env.CROSS_ANSWERS), msg]) diff --git a/buildtools/wafsamba/samba_deps.py b/buildtools/wafsamba/samba_deps.py index 3be9956..d252dc4 100644 --- a/buildtools/wafsamba/samba_deps.py +++ b/buildtools/wafsamba/samba_deps.py @@ -964,7 +964,8 @@ savedeps_version = 3 savedeps_inputs = ['samba_deps', 'samba_includes', 'local_include', 'local_include_first', 'samba_cflags', 'source', 'grouping_library', 'samba_ldflags', 'allow_undefined_symbols', 'use_global_deps', 'global_include' ] -savedeps_outputs = ['uselib', 'uselib_local', 'add_objects', 'includes', 'ccflags', 'ldflags', 'samba_deps_extended'] +savedeps_outputs = ['uselib', 'uselib_local', 'add_objects', 'includes', + 'ccflags', 'ldflags', 'samba_deps_extended', 'final_libs'] savedeps_outenv = ['INC_PATHS'] savedeps_envvars = ['NONSHARED_BINARIES', 'GLOBAL_DEPENDENCIES', 'EXTRA_CFLAGS', 'EXTRA_LDFLAGS', 'EXTRA_INCLUDES' ] savedeps_caches = ['GLOBAL_DEPENDENCIES', 'TARGET_TYPE', 'INIT_FUNCTIONS', 'SYSLIB_DEPS'] diff --git a/buildtools/wafsamba/samba_dist.py b/buildtools/wafsamba/samba_dist.py index aecacb7..654a168 100644 --- a/buildtools/wafsamba/samba_dist.py +++ b/buildtools/wafsamba/samba_dist.py @@ -86,12 +86,6 @@ def vcs_dir_contents(path): env = dict(os.environ) env["GIT_DIR"] = os.path.join(repo, ".git") break - elif os.path.isdir(os.path.join(repo, ".bzr")): - ls_files_cmd = [ 'bzr', 'ls', '--recursive', '--versioned', - os_path_relpath(path, repo)] - cwd = repo - env = None - break repo = os.path.dirname(repo) if repo == "/": raise Exception("unsupported or no vcs for %s" % path) diff --git a/buildtools/wafsamba/samba_git.py b/buildtools/wafsamba/samba_git.py new file mode 100644 index 0000000..c58a579 --- /dev/null +++ b/buildtools/wafsamba/samba_git.py @@ -0,0 +1,57 @@ +import os +import subprocess + +def find_git(env=None): + """Find the git binary.""" + if env is not None and 'GIT' in env: + return env['GIT'] + + # Get version from GIT + if os.path.exists("/usr/bin/git"): + # this is useful when doing make dist without configuring + return "/usr/bin/git" + + return None + + +def has_submodules(path): + """Check whether a source directory is git-versioned and has submodules. + + :param path: Path to Samba source directory + """ + return (os.path.isdir(os.path.join(path, ".git")) and + os.path.isfile(os.path.join(path, ".gitmodules"))) + + +def read_submodule_status(path, env=None): + """Check status of submodules. + + :param path: Path to git directory + :param env: Optional waf environment + :return: Yields tuples with submodule relpath and status + (one of: 'out-of-date', 'not-checked-out', 'up-to-date') + :raise RuntimeError: raised when parsing of 'git submodule status' output + fails. + """ + if not has_submodules(path): + # No point in running git. + return + git = find_git(env) + if git is None: + return + p = subprocess.Popen([git, "submodule", "status"], stdout=subprocess.PIPE, + cwd=path) + (stdout, stderr) = p.communicate(None) + for l in stdout.splitlines(): + l = l.rstrip() + status = l[0] + l = l[1:] + parts = l.split(" ") + if len(parts) > 2 and status in ("-", "+"): + yield (parts[1], "out-of-date") + elif len(parts) == 2 and status == "-": + yield (parts[1], "not-checked-out") + elif len(parts) > 2 and status == " ": + yield (parts[1], "up-to-date") + else: + raise RuntimeError("Unable to parse submodule status: %r, %r" % (status, parts)) diff --git a/buildtools/wafsamba/samba_install.py b/buildtools/wafsamba/samba_install.py index aa7f143..3d0c23a 100644 --- a/buildtools/wafsamba/samba_install.py +++ b/buildtools/wafsamba/samba_install.py @@ -59,90 +59,97 @@ def install_library(self): bld = self.bld - install_ldflags = install_rpath(self) - build_ldflags = build_rpath(bld) - - if not Options.is_install or not getattr(self, 'samba_install', True): - # just need to set the build rpath if we are not installing - self.env.RPATH = build_ldflags - return - - # setup the install path, expanding variables - install_path = getattr(self, 'samba_inst_path', None) - if install_path is None: - if getattr(self, 'private_library', False): - install_path = '${PRIVATELIBDIR}' - else: - install_path = '${LIBDIR}' - install_path = bld.EXPAND_VARIABLES(install_path) - - target_name = self.target - - if install_ldflags != build_ldflags: - # we will be creating a new target name, and using that for the - # install link. That stops us from overwriting the existing build - # target, which has different ldflags - self.done_install_library = True - t = self.clone('default') - t.posted = False - t.target += '.inst' - self.env.RPATH = build_ldflags - else: - t = self - - t.env.RPATH = install_ldflags + default_env = bld.all_envs['default'] + try: + if self.env['IS_EXTRA_PYTHON']: + bld.all_envs['default'] = bld.all_envs['extrapython'] - dev_link = None + install_ldflags = install_rpath(self) + build_ldflags = build_rpath(bld) - # in the following the names are: - # - inst_name is the name with .inst. in it, in the build - # directory - # - install_name is the name in the install directory - # - install_link is a symlink in the install directory, to install_name + if not Options.is_install or not getattr(self, 'samba_install', True): + # just need to set the build rpath if we are not installing + self.env.RPATH = build_ldflags + return - if getattr(self, 'samba_realname', None): - install_name = self.samba_realname - install_link = None - if getattr(self, 'soname', ''): - install_link = self.soname - if getattr(self, 'samba_type', None) == 'PYTHON': - inst_name = bld.make_libname(t.target, nolibprefix=True, python=True) + # setup the install path, expanding variables + install_path = getattr(self, 'samba_inst_path', None) + if install_path is None: + if getattr(self, 'private_library', False): + install_path = '${PRIVATELIBDIR}' + else: + install_path = '${LIBDIR}' + install_path = bld.EXPAND_VARIABLES(install_path) + + target_name = self.target + + if install_ldflags != build_ldflags: + # we will be creating a new target name, and using that for the + # install link. That stops us from overwriting the existing build + # target, which has different ldflags + self.done_install_library = True + t = self.clone(self.env) + t.posted = False + t.target += '.inst' + self.env.RPATH = build_ldflags else: + t = self + + t.env.RPATH = install_ldflags + + dev_link = None + + # in the following the names are: + # - inst_name is the name with .inst. in it, in the build + # directory + # - install_name is the name in the install directory + # - install_link is a symlink in the install directory, to install_name + + if getattr(self, 'samba_realname', None): + install_name = self.samba_realname + install_link = None + if getattr(self, 'soname', ''): + install_link = self.soname + if getattr(self, 'samba_type', None) == 'PYTHON': + inst_name = bld.make_libname(t.target, nolibprefix=True, python=True) + else: + inst_name = bld.make_libname(t.target) + elif self.vnum: + vnum_base = self.vnum.split('.')[0] + install_name = bld.make_libname(target_name, version=self.vnum) + install_link = bld.make_libname(target_name, version=vnum_base) + inst_name = bld.make_libname(t.target) + if not self.private_library: + # only generate the dev link for non-bundled libs + dev_link = bld.make_libname(target_name) + elif getattr(self, 'soname', ''): + install_name = bld.make_libname(target_name) + install_link = self.soname inst_name = bld.make_libname(t.target) - elif self.vnum: - vnum_base = self.vnum.split('.')[0] - install_name = bld.make_libname(target_name, version=self.vnum) - install_link = bld.make_libname(target_name, version=vnum_base) - inst_name = bld.make_libname(t.target) - if not self.private_library: - # only generate the dev link for non-bundled libs - dev_link = bld.make_libname(target_name) - elif getattr(self, 'soname', ''): - install_name = bld.make_libname(target_name) - install_link = self.soname - inst_name = bld.make_libname(t.target) - else: - install_name = bld.make_libname(target_name) - install_link = None - inst_name = bld.make_libname(t.target) - - if t.env.SONAME_ST: - # ensure we get the right names in the library - if install_link: - t.env.append_value('LINKFLAGS', t.env.SONAME_ST % install_link) else: - t.env.append_value('LINKFLAGS', t.env.SONAME_ST % install_name) - t.env.SONAME_ST = '' + install_name = bld.make_libname(target_name) + install_link = None + inst_name = bld.make_libname(t.target) - # tell waf to install the library - bld.install_as(os.path.join(install_path, install_name), - os.path.join(self.path.abspath(bld.env), inst_name), - chmod=MODE_755) - if install_link and install_link != install_name: - # and the symlink if needed - bld.symlink_as(os.path.join(install_path, install_link), os.path.basename(install_name)) - if dev_link: - bld.symlink_as(os.path.join(install_path, dev_link), os.path.basename(install_name)) + if t.env.SONAME_ST: + # ensure we get the right names in the library + if install_link: + t.env.append_value('LINKFLAGS', t.env.SONAME_ST % install_link) + else: + t.env.append_value('LINKFLAGS', t.env.SONAME_ST % install_name) + t.env.SONAME_ST = '' + + # tell waf to install the library + bld.install_as(os.path.join(install_path, install_name), + os.path.join(self.path.abspath(bld.env), inst_name), + chmod=MODE_755) + if install_link and install_link != install_name: + # and the symlink if needed + bld.symlink_as(os.path.join(install_path, install_link), os.path.basename(install_name)) + if dev_link: + bld.symlink_as(os.path.join(install_path, dev_link), os.path.basename(install_name)) + finally: + bld.all_envs['default'] = default_env @feature('cshlib') diff --git a/buildtools/wafsamba/samba_patterns.py b/buildtools/wafsamba/samba_patterns.py index b4427d3..0469992 100644 --- a/buildtools/wafsamba/samba_patterns.py +++ b/buildtools/wafsamba/samba_patterns.py @@ -140,9 +140,9 @@ def write_build_options_footer(fp): fp.write(" output(screen, \" sizeof(int): %lu\\n\",(unsigned long)sizeof(int));\n") fp.write(" output(screen, \" sizeof(long): %lu\\n\",(unsigned long)sizeof(long));\n") fp.write(" output(screen, \" sizeof(long long): %lu\\n\",(unsigned long)sizeof(long long));\n") - fp.write(" output(screen, \" sizeof(uint8): %lu\\n\",(unsigned long)sizeof(uint8));\n") - fp.write(" output(screen, \" sizeof(uint16): %lu\\n\",(unsigned long)sizeof(uint16));\n") - fp.write(" output(screen, \" sizeof(uint32): %lu\\n\",(unsigned long)sizeof(uint32));\n") + fp.write(" output(screen, \" sizeof(uint8_t): %lu\\n\",(unsigned long)sizeof(uint8_t));\n") + fp.write(" output(screen, \" sizeof(uint16_t): %lu\\n\",(unsigned long)sizeof(uint16_t));\n") + fp.write(" output(screen, \" sizeof(uint32_t): %lu\\n\",(unsigned long)sizeof(uint32_t));\n") fp.write(" output(screen, \" sizeof(short): %lu\\n\",(unsigned long)sizeof(short));\n") fp.write(" output(screen, \" sizeof(void*): %lu\\n\",(unsigned long)sizeof(void*));\n") fp.write(" output(screen, \" sizeof(size_t): %lu\\n\",(unsigned long)sizeof(size_t));\n") diff --git a/buildtools/wafsamba/samba_python.py b/buildtools/wafsamba/samba_python.py index a371b43..a8f780f 100644 --- a/buildtools/wafsamba/samba_python.py +++ b/buildtools/wafsamba/samba_python.py @@ -9,20 +9,70 @@ from Configure import conf @conf def SAMBA_CHECK_PYTHON(conf, mandatory=True, version=(2,4,2)): # enable tool to build python extensions + if conf.env.HAVE_PYTHON_H: + conf.check_python_version(version) + return + + interpreters = [] + + if conf.env['EXTRA_PYTHON']: + conf.all_envs['extrapython'] = conf.env.copy() + conf.setenv('extrapython') + conf.env['PYTHON'] = conf.env['EXTRA_PYTHON'] + conf.env['IS_EXTRA_PYTHON'] = 'yes' + conf.find_program('python', var='PYTHON', mandatory=True) + conf.check_tool('python') + try: + conf.check_python_version((3, 3, 0)) + except Exception: + Logs.warn('extra-python needs to be Python 3.3 or later') + raise + interpreters.append(conf.env['PYTHON']) + conf.setenv('default') + conf.find_program('python', var='PYTHON', mandatory=mandatory) conf.check_tool('python') path_python = conf.find_program('python') conf.env.PYTHON_SPECIFIED = (conf.env.PYTHON != path_python) conf.check_python_version(version) + interpreters.append(conf.env['PYTHON']) + conf.env.python_interpreters = interpreters + + @conf def SAMBA_CHECK_PYTHON_HEADERS(conf, mandatory=True): if conf.env["python_headers_checked"] == []: - conf.check_python_headers(mandatory) + if conf.env['EXTRA_PYTHON']: + conf.setenv('extrapython') + _check_python_headers(conf, mandatory=True) + conf.setenv('default') + + _check_python_headers(conf, mandatory) conf.env["python_headers_checked"] = "yes" + + if conf.env['EXTRA_PYTHON']: + extraversion = conf.all_envs['extrapython']['PYTHON_VERSION'] + if extraversion == conf.env['PYTHON_VERSION']: + raise Utils.WafError("extrapython %s is same as main python %s" % ( + extraversion, conf.env['PYTHON_VERSION'])) else: conf.msg("python headers", "using cache") + # we don't want PYTHONDIR in config.h, as otherwise changing + # --prefix causes a complete rebuild + del(conf.env.defines['PYTHONDIR']) + del(conf.env.defines['PYTHONARCHDIR']) + +def _check_python_headers(conf, mandatory): + conf.check_python_headers(mandatory=mandatory) + + if conf.env['PYTHON_VERSION'] > '3': + abi_pattern = os.path.splitext(conf.env['pyext_PATTERN'])[0] + conf.env['PYTHON_SO_ABI_FLAG'] = abi_pattern % '' + else: + conf.env['PYTHON_SO_ABI_FLAG'] = '' + def SAMBA_PYTHON(bld, name, source='', @@ -38,6 +88,9 @@ def SAMBA_PYTHON(bld, name, enabled=True): '''build a python extension for Samba''' + if bld.env['IS_EXTRA_PYTHON']: + name = 'extra-' + name + # when we support static python modules we'll need to gather # the list from all the SAMBA_PYTHON() targets if init_function_sentinel is not None: @@ -64,8 +117,35 @@ def SAMBA_PYTHON(bld, name, target_type='PYTHON', install_path='${PYTHONARCHDIR}', allow_undefined_symbols=True, - allow_warnings=True, install=install, enabled=enabled) Build.BuildContext.SAMBA_PYTHON = SAMBA_PYTHON + + +def pyembed_libname(bld, name, extrapython=False): + return name + bld.env['PYTHON_SO_ABI_FLAG'] + +Build.BuildContext.pyembed_libname = pyembed_libname + + +def gen_python_environments(bld, extra_env_vars=()): + """Generate all Python environments + + To be used in a for loop. Normally, the loop body will be executed once. + + When --extra-python is used, the body will additionaly be executed + with the extra-python environment active. + """ + yield + + if bld.env['EXTRA_PYTHON']: + copied = ('GLOBAL_DEPENDENCIES', 'TARGET_TYPE') + tuple(extra_env_vars) + for name in copied: + bld.all_envs['extrapython'][name] = bld.all_envs['default'][name] + default_env = bld.all_envs['default'] + bld.all_envs['default'] = bld.all_envs['extrapython'] + yield + bld.all_envs['default'] = default_env + +Build.BuildContext.gen_python_environments = gen_python_environments diff --git a/buildtools/wafsamba/samba_utils.py b/buildtools/wafsamba/samba_utils.py index e8bc0f3..540fe44 100644 --- a/buildtools/wafsamba/samba_utils.py +++ b/buildtools/wafsamba/samba_utils.py @@ -386,6 +386,22 @@ def RUN_COMMAND(cmd, return -1 +def RUN_PYTHON_TESTS(testfiles, pythonpath=None): + env = LOAD_ENVIRONMENT() + if pythonpath is None: + pythonpath = os.path.join(Utils.g_module.blddir, 'python') + result = 0 + for interp in env.python_interpreters: + for testfile in testfiles: + cmd = "PYTHONPATH=%s %s %s" % (pythonpath, interp, testfile) + print('Running Python test with %s: %s' % (interp, testfile)) + ret = RUN_COMMAND(cmd) + if ret: + print('Python test failed: %s' % cmd) + result = ret + return result + + # make sure we have md5. some systems don't have it try: from hashlib import md5 diff --git a/buildtools/wafsamba/samba_version.py b/buildtools/wafsamba/samba_version.py index 67ff232..bb0be96 100644 --- a/buildtools/wafsamba/samba_version.py +++ b/buildtools/wafsamba/samba_version.py @@ -1,68 +1,16 @@ import os import Utils import samba_utils -import sys - -def bzr_version_summary(path): - try: - import bzrlib - except ImportError: - return ("BZR-UNKNOWN", {}) - - import bzrlib.ui - bzrlib.ui.ui_factory = bzrlib.ui.make_ui_for_terminal( - sys.stdin, sys.stdout, sys.stderr) - from bzrlib import branch, osutils, workingtree - from bzrlib.plugin import load_plugins - load_plugins() - - b = branch.Branch.open(path) - (revno, revid) = b.last_revision_info() - rev = b.repository.get_revision(revid) - - fields = { - "BZR_REVISION_ID": revid, - "BZR_REVNO": revno, - "COMMIT_DATE": osutils.format_date_with_offset_in_original_timezone(rev.timestamp, - rev.timezone or 0), - "COMMIT_TIME": int(rev.timestamp), - "BZR_BRANCH": rev.properties.get("branch-nick", ""), - } - - # If possible, retrieve the git sha - try: - from bzrlib.plugins.git.object_store import get_object_store - except ImportError: - # No git plugin - ret = "BZR-%d" % revno - else: - store = get_object_store(b.repository) - store.lock_read() - try: - full_rev = store._lookup_revision_sha1(revid) - finally: - store.unlock() - fields["GIT_COMMIT_ABBREV"] = full_rev[:7] - fields["GIT_COMMIT_FULLREV"] = full_rev - ret = "GIT-" + fields["GIT_COMMIT_ABBREV"] - - if workingtree.WorkingTree.open(path).has_changes(): - fields["COMMIT_IS_CLEAN"] = 0 - ret += "+" - else: - fields["COMMIT_IS_CLEAN"] = 1 - return (ret, fields) - +from samba_git import find_git def git_version_summary(path, env=None): - # Get version from GIT - if not 'GIT' in env and os.path.exists("/usr/bin/git"): - # this is useful when doing make dist without configuring - env.GIT = "/usr/bin/git" + git = find_git(env) - if not 'GIT' in env: + if git is None: return ("GIT-UNKNOWN", {}) + env.GIT = git + environ = dict(os.environ) environ["GIT_DIR"] = '%s/.git' % path environ["GIT_WORK_TREE"] = path @@ -200,8 +148,6 @@ also accepted as dictionary entries here self.vcs_fields = {} elif os.path.exists(os.path.join(path, ".git")): suffix, self.vcs_fields = git_version_summary(path, env=env) - elif os.path.exists(os.path.join(path, ".bzr")): - suffix, self.vcs_fields = bzr_version_summary(path) elif os.path.exists(os.path.join(path, ".distversion")): suffix, self.vcs_fields = distversion_version_summary(path) else: diff --git a/buildtools/wafsamba/wafsamba.py b/buildtools/wafsamba/wafsamba.py index d7e482c..c27241e 100644 --- a/buildtools/wafsamba/wafsamba.py +++ b/buildtools/wafsamba/wafsamba.py @@ -143,6 +143,9 @@ def SAMBA_LIBRARY(bld, libname, source, enabled=True): '''define a Samba library''' + if pyembed and bld.env['IS_EXTRA_PYTHON']: + public_headers = pc_files = None + if LIB_MUST_BE_PRIVATE(bld, libname): private_library=True @@ -217,10 +220,10 @@ def SAMBA_LIBRARY(bld, libname, source, if vnum is None and soname is None: raise Utils.WafError("public library '%s' must have a vnum" % libname) - if pc_files is None: + if pc_files is None and not bld.env['IS_EXTRA_PYTHON']: raise Utils.WafError("public library '%s' must have pkg-config file" % libname) - if public_headers is None: + if public_headers is None and not bld.env['IS_EXTRA_PYTHON']: raise Utils.WafError("public library '%s' must have header files" % libname) @@ -239,6 +242,8 @@ def SAMBA_LIBRARY(bld, libname, source, bundled_extension, private_library) ldflags = TO_LIST(ldflags) + if bld.env['ENABLE_RELRO'] is True: + ldflags.extend(TO_LIST('-Wl,-z,relro,-z,now')) features = 'cc cshlib symlink_lib install_lib' if pyext: diff --git a/buildtools/wafsamba/wscript b/buildtools/wafsamba/wscript index 694147e..d6bb688 100755 --- a/buildtools/wafsamba/wscript +++ b/buildtools/wafsamba/wscript @@ -195,6 +195,12 @@ def set_options(opt): help='tag release in git at the same time', type='string', action='store', dest='TAG_RELEASE') + opt.add_option('--extra-python', type=str, + help=("build selected libraries for the specified " + "additional version of Python " + "(example: --extra-python=/usr/bin/python3)"), + metavar="PYTHON", dest='EXTRA_PYTHON', default=None) + @wafsamba.runonce def configure(conf): @@ -266,6 +272,8 @@ def configure(conf): conf.env.AUTOCONF_HOST = Options.options.AUTOCONF_HOST conf.env.AUTOCONF_PROGRAM_PREFIX = Options.options.AUTOCONF_PROGRAM_PREFIX + conf.env.EXTRA_PYTHON = Options.options.EXTRA_PYTHON + if (conf.env.AUTOCONF_HOST and conf.env.AUTOCONF_BUILD and conf.env.AUTOCONF_BUILD != conf.env.AUTOCONF_HOST): diff --git a/common/traverse.c b/common/traverse.c index 618670f..e18e3c3 100644 --- a/common/traverse.c +++ b/common/traverse.c @@ -245,13 +245,25 @@ _PUBLIC_ int tdb_traverse(struct tdb_context *tdb, tdb_traverse_func fn, void *private_data) { struct tdb_traverse_lock tl = { NULL, 0, 0, F_WRLCK }; + enum tdb_lock_flags lock_flags; int ret; if (tdb->read_only || tdb->traverse_read) { return tdb_traverse_read(tdb, fn, private_data); } - if (tdb_transaction_lock(tdb, F_WRLCK, TDB_LOCK_WAIT)) { + lock_flags = TDB_LOCK_WAIT; + + if (tdb->allrecord_lock.count != 0) { + /* + * This avoids a deadlock between tdb_lockall() and + * tdb_traverse(). See + * https://bugzilla.samba.org/show_bug.cgi?id=11381 + */ + lock_flags = TDB_LOCK_NOWAIT; + } + + if (tdb_transaction_lock(tdb, F_WRLCK, lock_flags)) { return -1; } diff --git a/lib/replace/replace.c b/lib/replace/replace.c index 9fae44a..dccf514 100644 --- a/lib/replace/replace.c +++ b/lib/replace/replace.c @@ -518,11 +518,10 @@ long long int rep_strtoll(const char *str, char **endptr, int base) } #else #ifdef HAVE_BSD_STRTOLL -#ifdef HAVE_STRTOQ long long int rep_strtoll(const char *str, char **endptr, int base) { - long long int nb = strtoq(str, endptr, base); - /* In linux EINVAL is only returned if base is not ok */ + long long int nb = strtoll(str, endptr, base); + /* With glibc EINVAL is only returned if base is not ok */ if (errno == EINVAL) { if (base == 0 || (base >1 && base <37)) { /* Base was ok so it's because we were not @@ -534,9 +533,6 @@ long long int rep_strtoll(const char *str, char **endptr, int base) } return nb; } -#else -#error "You need the strtoq function" -#endif /* HAVE_STRTOQ */ #endif /* HAVE_BSD_STRTOLL */ #endif /* HAVE_STRTOLL */ diff --git a/lib/replace/system/threads.h b/lib/replace/system/threads.h index 25d3502..fe6d0fb 100644 --- a/lib/replace/system/threads.h +++ b/lib/replace/system/threads.h @@ -29,15 +29,12 @@ #if defined(HAVE_PTHREAD_MUTEXATTR_SETROBUST_NP) && \ !defined(HAVE_PTHREAD_MUTEXATTR_SETROBUST) - #define pthread_mutexattr_setrobust pthread_mutexattr_setrobust_np +#endif -/* - * We assume that PTHREAD_MUTEX_ROBUST_NP goes along with - * pthread_mutexattr_setrobust_np() - */ +#if defined(HAVE_DECL_PTHREAD_MUTEX_ROBUST_NP) && \ + !defined(HAVE_DECL_PTHREAD_MUTEX_ROBUST) #define PTHREAD_MUTEX_ROBUST PTHREAD_MUTEX_ROBUST_NP - #endif #if defined(HAVE_PTHREAD_MUTEX_CONSISTENT_NP) && \ diff --git a/lib/replace/test/testsuite.c b/lib/replace/test/testsuite.c index 017b8ed..961b77d 100644 --- a/lib/replace/test/testsuite.c +++ b/lib/replace/test/testsuite.c @@ -69,19 +69,23 @@ static int test_ftruncate(void) } if (ftruncate(fd, size) != 0) { printf("failure: ftruncate [\n%s\n]\n", strerror(errno)); + close(fd); return false; } if (fstat(fd, &st) != 0) { printf("failure: ftruncate [\nfstat failed - %s\n]\n", strerror(errno)); + close(fd); return false; } if (st.st_size != size) { printf("failure: ftruncate [\ngave wrong size %d - expected %d\n]\n", (int)st.st_size, size); + close(fd); return false; } unlink(TESTFILE); printf("success: ftruncate\n"); + close(fd); return true; } diff --git a/lib/replace/wscript b/lib/replace/wscript index 1b156fa..516db2f 100644 --- a/lib/replace/wscript +++ b/lib/replace/wscript @@ -136,6 +136,7 @@ def configure(conf): conf.CHECK_TYPE_IN('sa_family_t', 'sys/socket.h') conf.CHECK_TYPE_IN('sig_atomic_t', 'signal.h', define='HAVE_SIG_ATOMIC_T_TYPE') + conf.CHECK_FUNCS('sigsetmask siggetmask sigprocmask sigblock sigaction sigset') conf.CHECK_FUNCS_IN('''inet_ntoa inet_aton inet_ntop inet_pton connect gethostbyname getaddrinfo getnameinfo freeaddrinfo gai_strerror socketpair''', @@ -447,21 +448,15 @@ removeea setea if conf.CONFIG_SET('HAVE_PTHREAD'): - conf.CHECK_DECLS('pthread_mutexattr_setrobust', headers='pthread.h') - if not conf.CONFIG_SET('HAVE_DECL_PTHREAD_MUTEXATTR_SETROBUST'): - conf.CHECK_DECLS('pthread_mutexattr_setrobust_np', - headers='pthread.h') - conf.CHECK_FUNCS_IN('pthread_mutexattr_setrobust', 'pthread', checklibc=True, headers='pthread.h') if not conf.CONFIG_SET('HAVE_PTHREAD_MUTEXATTR_SETROBUST'): conf.CHECK_FUNCS_IN('pthread_mutexattr_setrobust_np', 'pthread', checklibc=True, headers='pthread.h') - conf.CHECK_DECLS('pthread_mutex_consistent', headers='pthread.h') - if not conf.CONFIG_SET('HAVE_DECL_PTHREAD_MUTEX_CONSISTENT'): - conf.CHECK_DECLS('pthread_mutex_consistent_np', - headers='pthread.h') + conf.CHECK_DECLS('PTHREAD_MUTEX_ROBUST', headers='pthread.h') + if not conf.CONFIG_SET('HAVE_DECL_PTHREAD_MUTEX_ROBUST'): + conf.CHECK_DECLS('PTHREAD_MUTEX_ROBUST_NP', headers='pthread.h') conf.CHECK_FUNCS_IN('pthread_mutex_consistent', 'pthread', checklibc=True, headers='pthread.h') @@ -471,6 +466,8 @@ removeea setea if ((conf.CONFIG_SET('HAVE_PTHREAD_MUTEXATTR_SETROBUST') or conf.CONFIG_SET('HAVE_PTHREAD_MUTEXATTR_SETROBUST_NP')) and + (conf.CONFIG_SET('HAVE_DECL_PTHREAD_MUTEX_ROBUST') or + conf.CONFIG_SET('HAVE_DECL_PTHREAD_MUTEX_ROBUST_NP')) and (conf.CONFIG_SET('HAVE_PTHREAD_MUTEX_CONSISTENT') or conf.CONFIG_SET('HAVE_PTHREAD_MUTEX_CONSISTENT_NP'))): conf.DEFINE('HAVE_ROBUST_MUTEXES', 1) @@ -31,13 +31,25 @@ /* Include tdb headers */ #include <tdb.h> +#if PY_MAJOR_VERSION >= 3 +#define PyStr_FromString PyUnicode_FromString +#define PyStr_FromFormat PyUnicode_FromFormat +#define PyInt_FromLong PyLong_FromLong +#define PyInt_Check PyLong_Check +#define PyInt_AsLong PyLong_AsLong +#define Py_TPFLAGS_HAVE_ITER 0 +#else +#define PyStr_FromString PyString_FromString +#define PyStr_FromFormat PyString_FromFormat +#endif + typedef struct { PyObject_HEAD TDB_CONTEXT *ctx; bool closed; } PyTdbObject; -staticforward PyTypeObject PyTdb; +static PyTypeObject PyTdb; static void PyErr_SetTDBError(TDB_CONTEXT *tdb) { @@ -45,21 +57,21 @@ static void PyErr_SetTDBError(TDB_CONTEXT *tdb) Py_BuildValue("(i,s)", tdb_error(tdb), tdb_errorstr(tdb))); } -static TDB_DATA PyString_AsTDB_DATA(PyObject *data) +static TDB_DATA PyBytes_AsTDB_DATA(PyObject *data) { TDB_DATA ret; - ret.dptr = (unsigned char *)PyString_AsString(data); - ret.dsize = PyString_Size(data); + ret.dptr = (unsigned char *)PyBytes_AsString(data); + ret.dsize = PyBytes_Size(data); return ret; } -static PyObject *PyString_FromTDB_DATA(TDB_DATA data) +static PyObject *PyBytes_FromTDB_DATA(TDB_DATA data) { if (data.dptr == NULL && data.dsize == 0) { Py_RETURN_NONE; } else { - PyObject *ret = PyString_FromStringAndSize((const char *)data.dptr, - data.dsize); + PyObject *ret = PyBytes_FromStringAndSize((const char *)data.dptr, + data.dsize); free(data.dptr); return ret; } @@ -233,11 +245,11 @@ static PyObject *obj_get(PyTdbObject *self, PyObject *args) if (!PyArg_ParseTuple(args, "O", &py_key)) return NULL; - key = PyString_AsTDB_DATA(py_key); + key = PyBytes_AsTDB_DATA(py_key); if (!key.dptr) return NULL; - return PyString_FromTDB_DATA(tdb_fetch(self->ctx, key)); + return PyBytes_FromTDB_DATA(tdb_fetch(self->ctx, key)); } static PyObject *obj_append(PyTdbObject *self, PyObject *args) @@ -251,10 +263,10 @@ static PyObject *obj_append(PyTdbObject *self, PyObject *args) if (!PyArg_ParseTuple(args, "OO", &py_key, &py_data)) return NULL; - key = PyString_AsTDB_DATA(py_key); + key = PyBytes_AsTDB_DATA(py_key); if (!key.dptr) return NULL; - data = PyString_AsTDB_DATA(py_data); + data = PyBytes_AsTDB_DATA(py_data); if (!data.dptr) return NULL; @@ -267,7 +279,7 @@ static PyObject *obj_firstkey(PyTdbObject *self) { PyErr_TDB_RAISE_IF_CLOSED(self); - return PyString_FromTDB_DATA(tdb_firstkey(self->ctx)); + return PyBytes_FromTDB_DATA(tdb_firstkey(self->ctx)); } static PyObject *obj_nextkey(PyTdbObject *self, PyObject *args) @@ -279,11 +291,11 @@ static PyObject *obj_nextkey(PyTdbObject *self, PyObject *args) if (!PyArg_ParseTuple(args, "O", &py_key)) return NULL; - key = PyString_AsTDB_DATA(py_key); + key = PyBytes_AsTDB_DATA(py_key); if (!key.dptr) return NULL; - return PyString_FromTDB_DATA(tdb_nextkey(self->ctx, key)); + return PyBytes_FromTDB_DATA(tdb_nextkey(self->ctx, key)); } static PyObject *obj_delete(PyTdbObject *self, PyObject *args) @@ -296,7 +308,7 @@ static PyObject *obj_delete(PyTdbObject *self, PyObject *args) if (!PyArg_ParseTuple(args, "O", &py_key)) return NULL; - key = PyString_AsTDB_DATA(py_key); + key = PyBytes_AsTDB_DATA(py_key); if (!key.dptr) return NULL; ret = tdb_delete(self->ctx, key); @@ -304,26 +316,42 @@ static PyObject *obj_delete(PyTdbObject *self, PyObject *args) Py_RETURN_NONE; } -static PyObject *obj_has_key(PyTdbObject *self, PyObject *args) +static int obj_contains(PyTdbObject *self, PyObject *py_key) { TDB_DATA key; int ret; + PyErr_TDB_RAISE_RETURN_MINUS_1_IF_CLOSED(self); + + key = PyBytes_AsTDB_DATA(py_key); + if (!key.dptr) { + PyErr_BadArgument(); + return -1; + } + ret = tdb_exists(self->ctx, key); + if (ret) + return 1; + return 0; +} + +#if PY_MAJOR_VERSION < 3 +static PyObject *obj_has_key(PyTdbObject *self, PyObject *args) +{ + int ret; PyObject *py_key; PyErr_TDB_RAISE_IF_CLOSED(self); if (!PyArg_ParseTuple(args, "O", &py_key)) return NULL; - key = PyString_AsTDB_DATA(py_key); - if (!key.dptr) + ret = obj_contains(self, py_key); + if (ret == -1) return NULL; - ret = tdb_exists(self->ctx, key); - if (ret != TDB_ERR_NOEXIST) { - PyErr_TDB_ERROR_IS_ERR_RAISE(ret, self->ctx); - } + if (ret) + Py_RETURN_TRUE; + Py_RETURN_FALSE; - return (ret == TDB_ERR_NOEXIST)?Py_False:Py_True; } +#endif static PyObject *obj_store(PyTdbObject *self, PyObject *args) { @@ -337,10 +365,10 @@ static PyObject *obj_store(PyTdbObject *self, PyObject *args) if (!PyArg_ParseTuple(args, "OO|i", &py_key, &py_value, &flag)) return NULL; - key = PyString_AsTDB_DATA(py_key); + key = PyBytes_AsTDB_DATA(py_key); if (!key.dptr) return NULL; - value = PyString_AsTDB_DATA(py_value); + value = PyBytes_AsTDB_DATA(py_value); if (!value.dptr) return NULL; @@ -389,7 +417,7 @@ static PyObject *tdb_iter_next(PyTdbIteratorObject *self) return NULL; current = self->current; self->current = tdb_nextkey(self->iteratee->ctx, self->current); - ret = PyString_FromTDB_DATA(current); + ret = PyBytes_FromTDB_DATA(current); return ret; } @@ -480,17 +508,23 @@ static PyMethodDef tdb_object_methods[] = { "Append data to an existing key." }, { "firstkey", (PyCFunction)obj_firstkey, METH_NOARGS, "S.firstkey() -> data\n" "Return the first key in this database." }, - { "nextkey", (PyCFunction)obj_nextkey, METH_NOARGS, "S.nextkey(key) -> data\n" + { "nextkey", (PyCFunction)obj_nextkey, METH_VARARGS, "S.nextkey(key) -> data\n" "Return the next key in this database." }, { "delete", (PyCFunction)obj_delete, METH_VARARGS, "S.delete(key) -> None\n" "Delete an entry." }, +#if PY_MAJOR_VERSION < 3 { "has_key", (PyCFunction)obj_has_key, METH_VARARGS, "S.has_key(key) -> None\n" "Check whether key exists in this database." }, +#endif { "store", (PyCFunction)obj_store, METH_VARARGS, "S.store(key, data, flag=REPLACE) -> None" "Store data." }, { "add_flags", (PyCFunction)obj_add_flags, METH_VARARGS, "S.add_flags(flags) -> None" }, { "remove_flags", (PyCFunction)obj_remove_flags, METH_VARARGS, "S.remove_flags(flags) -> None" }, +#if PY_MAJOR_VERSION >= 3 + { "keys", (PyCFunction)tdb_object_iter, METH_NOARGS, "S.iterkeys() -> iterator" }, +#else { "iterkeys", (PyCFunction)tdb_object_iter, METH_NOARGS, "S.iterkeys() -> iterator" }, +#endif { "clear", (PyCFunction)obj_clear, METH_NOARGS, "S.clear() -> None\n" "Wipe the entire database." }, { "repack", (PyCFunction)obj_repack, METH_NOARGS, "S.repack() -> None\n" @@ -538,7 +572,7 @@ static PyObject *obj_get_flags(PyTdbObject *self, void *closure) static PyObject *obj_get_filename(PyTdbObject *self, void *closure) { PyErr_TDB_RAISE_IF_CLOSED(self); - return PyString_FromString(tdb_name(self->ctx)); + return PyBytes_FromString(tdb_name(self->ctx)); } static PyObject *obj_get_seqnum(PyTdbObject *self, void *closure) @@ -547,6 +581,22 @@ static PyObject *obj_get_seqnum(PyTdbObject *self, void *closure) return PyInt_FromLong(tdb_get_seqnum(self->ctx)); } +static PyObject *obj_get_text(PyTdbObject *self, void *closure) +{ + PyObject *mod, *cls, *inst; + mod = PyImport_ImportModule("_tdb_text"); + if (mod == NULL) + return NULL; + cls = PyObject_GetAttrString(mod, "TdbTextWrapper"); + if (cls == NULL) { + Py_DECREF(mod); + return NULL; + } + inst = PyObject_CallFunction(cls, discard_const_p(char, "O"), self); + Py_DECREF(mod); + Py_DECREF(cls); + return inst; +} static PyGetSetDef tdb_object_getsetters[] = { { discard_const_p(char, "hash_size"), @@ -564,6 +614,8 @@ static PyGetSetDef tdb_object_getsetters[] = { discard_const_p(char, "The filename of this TDB file.") }, { discard_const_p(char, "seqnum"), (getter)obj_get_seqnum, NULL, NULL }, + { discard_const_p(char, "text"), + (getter)obj_get_text, NULL, NULL }, { NULL } }; @@ -571,9 +623,9 @@ static PyObject *tdb_object_repr(PyTdbObject *self) { PyErr_TDB_RAISE_IF_CLOSED(self); if (tdb_get_flags(self->ctx) & TDB_INTERNAL) { - return PyString_FromString("Tdb(<internal>)"); + return PyStr_FromString("Tdb(<internal>)"); } else { - return PyString_FromFormat("Tdb('%s')", tdb_name(self->ctx)); + return PyStr_FromFormat("Tdb('%s')", tdb_name(self->ctx)); } } @@ -581,27 +633,31 @@ static void tdb_object_dealloc(PyTdbObject *self) { if (!self->closed) tdb_close(self->ctx); - self->ob_type->tp_free(self); + Py_TYPE(self)->tp_free(self); } static PyObject *obj_getitem(PyTdbObject *self, PyObject *key) { TDB_DATA tkey, val; PyErr_TDB_RAISE_IF_CLOSED(self); - if (!PyString_Check(key)) { - PyErr_SetString(PyExc_TypeError, "Expected string as key"); + if (!PyBytes_Check(key)) { + PyErr_SetString(PyExc_TypeError, "Expected bytestring as key"); return NULL; } - tkey.dptr = (unsigned char *)PyString_AsString(key); - tkey.dsize = PyString_Size(key); + tkey.dptr = (unsigned char *)PyBytes_AsString(key); + tkey.dsize = PyBytes_Size(key); val = tdb_fetch(self->ctx, tkey); if (val.dptr == NULL) { - PyErr_SetString(PyExc_KeyError, "No such TDB entry"); + /* + * if the key doesn't exist raise KeyError(key) to be + * consistent with python dict + */ + PyErr_SetObject(PyExc_KeyError, key); return NULL; } else { - return PyString_FromTDB_DATA(val); + return PyBytes_FromTDB_DATA(val); } } @@ -610,22 +666,22 @@ static int obj_setitem(PyTdbObject *self, PyObject *key, PyObject *value) TDB_DATA tkey, tval; int ret; PyErr_TDB_RAISE_RETURN_MINUS_1_IF_CLOSED(self); - if (!PyString_Check(key)) { - PyErr_SetString(PyExc_TypeError, "Expected string as key"); + if (!PyBytes_Check(key)) { + PyErr_SetString(PyExc_TypeError, "Expected bytestring as key"); return -1; } - tkey = PyString_AsTDB_DATA(key); + tkey = PyBytes_AsTDB_DATA(key); if (value == NULL) { ret = tdb_delete(self->ctx, tkey); } else { - if (!PyString_Check(value)) { + if (!PyBytes_Check(value)) { PyErr_SetString(PyExc_TypeError, "Expected string as value"); return -1; } - tval = PyString_AsTDB_DATA(value); + tval = PyBytes_AsTDB_DATA(value); ret = tdb_store(self->ctx, tkey, tval, TDB_REPLACE); } @@ -642,6 +698,9 @@ static PyMappingMethods tdb_object_mapping = { .mp_subscript = (binaryfunc)obj_getitem, .mp_ass_subscript = (objobjargproc)obj_setitem, }; +static PySequenceMethods tdb_object_seq = { + .sq_contains = (objobjproc)obj_contains, +}; static PyTypeObject PyTdb = { .tp_name = "tdb.Tdb", .tp_basicsize = sizeof(PyTdbObject), @@ -652,6 +711,7 @@ static PyTypeObject PyTdb = { .tp_repr = (reprfunc)tdb_object_repr, .tp_dealloc = (destructor)tdb_object_dealloc, .tp_as_mapping = &tdb_object_mapping, + .tp_as_sequence = &tdb_object_seq, .tp_flags = Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE|Py_TPFLAGS_HAVE_ITER, .tp_iter = (getiterfunc)tdb_object_iter, }; @@ -662,46 +722,78 @@ static PyMethodDef tdb_methods[] = { { NULL } }; -void inittdb(void); -void inittdb(void) +#define MODULE_DOC "simple key-value database that supports multiple writers." + +#if PY_MAJOR_VERSION >= 3 +static struct PyModuleDef moduledef = { + PyModuleDef_HEAD_INIT, + .m_name = "tdb", + .m_doc = MODULE_DOC, + .m_size = -1, + .m_methods = tdb_methods, +}; +#endif + +PyObject* module_init(void); +PyObject* module_init(void) { PyObject *m; if (PyType_Ready(&PyTdb) < 0) - return; + return NULL; if (PyType_Ready(&PyTdbIterator) < 0) - return; + return NULL; - m = Py_InitModule3("tdb", tdb_methods, - "simple key-value database that supports multiple writers."); +#if PY_MAJOR_VERSION >= 3 + m = PyModule_Create(&moduledef); +#else + m = Py_InitModule3("tdb", tdb_methods, MODULE_DOC); +#endif if (m == NULL) - return; - - PyModule_AddObject(m, "REPLACE", PyInt_FromLong(TDB_REPLACE)); - PyModule_AddObject(m, "INSERT", PyInt_FromLong(TDB_INSERT)); - PyModule_AddObject(m, "MODIFY", PyInt_FromLong(TDB_MODIFY)); - - PyModule_AddObject(m, "DEFAULT", PyInt_FromLong(TDB_DEFAULT)); - PyModule_AddObject(m, "CLEAR_IF_FIRST", PyInt_FromLong(TDB_CLEAR_IF_FIRST)); - PyModule_AddObject(m, "INTERNAL", PyInt_FromLong(TDB_INTERNAL)); - PyModule_AddObject(m, "NOLOCK", PyInt_FromLong(TDB_NOLOCK)); - PyModule_AddObject(m, "NOMMAP", PyInt_FromLong(TDB_NOMMAP)); - PyModule_AddObject(m, "CONVERT", PyInt_FromLong(TDB_CONVERT)); - PyModule_AddObject(m, "BIGENDIAN", PyInt_FromLong(TDB_BIGENDIAN)); - PyModule_AddObject(m, "NOSYNC", PyInt_FromLong(TDB_NOSYNC)); - PyModule_AddObject(m, "SEQNUM", PyInt_FromLong(TDB_SEQNUM)); - PyModule_AddObject(m, "VOLATILE", PyInt_FromLong(TDB_VOLATILE)); - PyModule_AddObject(m, "ALLOW_NESTING", PyInt_FromLong(TDB_ALLOW_NESTING)); - PyModule_AddObject(m, "DISALLOW_NESTING", PyInt_FromLong(TDB_DISALLOW_NESTING)); - PyModule_AddObject(m, "INCOMPATIBLE_HASH", PyInt_FromLong(TDB_INCOMPATIBLE_HASH)); - - PyModule_AddObject(m, "__docformat__", PyString_FromString("restructuredText")); - - PyModule_AddObject(m, "__version__", PyString_FromString(PACKAGE_VERSION)); + return NULL; + + PyModule_AddIntConstant(m, "REPLACE", TDB_REPLACE); + PyModule_AddIntConstant(m, "INSERT", TDB_INSERT); + PyModule_AddIntConstant(m, "MODIFY", TDB_MODIFY); + + PyModule_AddIntConstant(m, "DEFAULT", TDB_DEFAULT); + PyModule_AddIntConstant(m, "CLEAR_IF_FIRST", TDB_CLEAR_IF_FIRST); + PyModule_AddIntConstant(m, "INTERNAL", TDB_INTERNAL); + PyModule_AddIntConstant(m, "NOLOCK", TDB_NOLOCK); + PyModule_AddIntConstant(m, "NOMMAP", TDB_NOMMAP); + PyModule_AddIntConstant(m, "CONVERT", TDB_CONVERT); + PyModule_AddIntConstant(m, "BIGENDIAN", TDB_BIGENDIAN); + PyModule_AddIntConstant(m, "NOSYNC", TDB_NOSYNC); + PyModule_AddIntConstant(m, "SEQNUM", TDB_SEQNUM); + PyModule_AddIntConstant(m, "VOLATILE", TDB_VOLATILE); + PyModule_AddIntConstant(m, "ALLOW_NESTING", TDB_ALLOW_NESTING); + PyModule_AddIntConstant(m, "DISALLOW_NESTING", TDB_DISALLOW_NESTING); + PyModule_AddIntConstant(m, "INCOMPATIBLE_HASH", TDB_INCOMPATIBLE_HASH); + + PyModule_AddStringConstant(m, "__docformat__", "restructuredText"); + + PyModule_AddStringConstant(m, "__version__", PACKAGE_VERSION); Py_INCREF(&PyTdb); PyModule_AddObject(m, "Tdb", (PyObject *)&PyTdb); Py_INCREF(&PyTdbIterator); + + return m; +} + + +#if PY_MAJOR_VERSION >= 3 +PyMODINIT_FUNC PyInit_tdb(void); +PyMODINIT_FUNC PyInit_tdb(void) +{ + return module_init(); +} +#else +void inittdb(void); +void inittdb(void) +{ + module_init(); } +#endif diff --git a/python/tests/simple.py b/python/tests/simple.py index 4751f9b..b3136dd 100644 --- a/python/tests/simple.py +++ b/python/tests/simple.py @@ -6,9 +6,12 @@ # Copyright (C) 2007-2008 Jelmer Vernooij <jelmer@samba.org> # Published under the GNU LGPLv3 or later -import tdb +import sys +import os +import tempfile from unittest import TestCase -import os, tempfile + +import tdb class OpenTdbTests(TestCase): @@ -43,19 +46,18 @@ class InternalTdbTests(TestCase): self.assertEquals(repr(self.tdb), "Tdb(<internal>)") -class SimpleTdbTests(TestCase): +class CommonTdbTests(TestCase): + """Tests common to both the text & bytes interfaces""" + + use_text = False def setUp(self): - super(SimpleTdbTests, self).setUp() + super(CommonTdbTests, self).setUp() self.tdb = tdb.Tdb(tempfile.mkstemp()[1], 0, tdb.DEFAULT, os.O_CREAT|os.O_RDWR) self.assertNotEqual(None, self.tdb) - - def tearDown(self): - del self.tdb - - def test_repr(self): - self.assertTrue(repr(self.tdb).startswith("Tdb('")) + if self.use_text: + self.tdb = self.tdb.text def test_lockall(self): self.tdb.lock_all() @@ -74,27 +76,6 @@ class SimpleTdbTests(TestCase): def test_reopen(self): self.tdb.reopen() - def test_store(self): - self.tdb.store("bar", "bla") - self.assertEquals("bla", self.tdb.get("bar")) - - def test_getitem(self): - self.tdb["bar"] = "foo" - self.tdb.reopen() - self.assertEquals("foo", self.tdb["bar"]) - - def test_delete(self): - self.tdb["bar"] = "foo" - del self.tdb["bar"] - self.assertRaises(KeyError, lambda: self.tdb["bar"]) - - def test_contains(self): - self.tdb["bla"] = "bloe" - self.assertTrue("bla" in self.tdb) - - def test_keyerror(self): - self.assertRaises(KeyError, lambda: self.tdb["bla"]) - def test_hash_size(self): self.tdb.hash_size @@ -107,52 +88,101 @@ class SimpleTdbTests(TestCase): def test_name(self): self.tdb.filename + def test_add_flags(self): + self.tdb.add_flags(tdb.NOMMAP) + self.tdb.remove_flags(tdb.NOMMAP) + + +class TextCommonTdbTests(CommonTdbTests): + + use_text = True + + +class SimpleTdbTests(TestCase): + + def setUp(self): + super(SimpleTdbTests, self).setUp() + self.tdb = tdb.Tdb(tempfile.mkstemp()[1], 0, tdb.DEFAULT, + os.O_CREAT|os.O_RDWR) + self.assertNotEqual(None, self.tdb) + + def test_repr(self): + self.assertTrue(repr(self.tdb).startswith("Tdb('")) + + def test_store(self): + self.tdb.store(b"bar", b"bla") + self.assertEquals(b"bla", self.tdb.get(b"bar")) + + def test_getitem(self): + self.tdb[b"bar"] = b"foo" + self.tdb.reopen() + self.assertEquals(b"foo", self.tdb[b"bar"]) + + def test_delete(self): + self.tdb[b"bar"] = b"foo" + del self.tdb[b"bar"] + self.assertRaises(KeyError, lambda: self.tdb[b"bar"]) + + def test_contains(self): + self.tdb[b"bla"] = b"bloe" + self.assertTrue(b"bla" in self.tdb) + self.assertFalse(b"qwertyuiop" in self.tdb) + if sys.version_info < (3, 0): + self.assertTrue(self.tdb.has_key(b"bla")) + self.assertFalse(self.tdb.has_key(b"qwertyuiop")) + + def test_keyerror(self): + self.assertRaises(KeyError, lambda: self.tdb[b"bla"]) + def test_iterator(self): - self.tdb["bla"] = "1" - self.tdb["brainslug"] = "2" + self.tdb[b"bla"] = b"1" + self.tdb[b"brainslug"] = b"2" l = list(self.tdb) l.sort() - self.assertEquals(["bla", "brainslug"], l) + self.assertEquals([b"bla", b"brainslug"], l) def test_transaction_cancel(self): - self.tdb["bloe"] = "2" + self.tdb[b"bloe"] = b"2" self.tdb.transaction_start() - self.tdb["bloe"] = "1" + self.tdb[b"bloe"] = b"1" self.tdb.transaction_cancel() - self.assertEquals("2", self.tdb["bloe"]) + self.assertEquals(b"2", self.tdb[b"bloe"]) def test_transaction_commit(self): - self.tdb["bloe"] = "2" + self.tdb[b"bloe"] = b"2" self.tdb.transaction_start() - self.tdb["bloe"] = "1" + self.tdb[b"bloe"] = b"1" self.tdb.transaction_commit() - self.assertEquals("1", self.tdb["bloe"]) + self.assertEquals(b"1", self.tdb[b"bloe"]) def test_transaction_prepare_commit(self): - self.tdb["bloe"] = "2" + self.tdb[b"bloe"] = b"2" self.tdb.transaction_start() - self.tdb["bloe"] = "1" + self.tdb[b"bloe"] = b"1" self.tdb.transaction_prepare_commit() self.tdb.transaction_commit() - self.assertEquals("1", self.tdb["bloe"]) + self.assertEquals(b"1", self.tdb[b"bloe"]) def test_iterkeys(self): - self.tdb["bloe"] = "2" - self.tdb["bla"] = "25" - i = self.tdb.iterkeys() - self.assertEquals(set(["bloe", "bla"]), set([i.next(), i.next()])) + self.tdb[b"bloe"] = b"2" + self.tdb[b"bla"] = b"25" + if sys.version_info >= (3, 0): + i = self.tdb.keys() + else: + i = self.tdb.iterkeys() + self.assertEquals(set([b"bloe", b"bla"]), set([next(i), next(i)])) def test_clear(self): - self.tdb["bloe"] = "2" - self.tdb["bla"] = "25" + self.tdb[b"bloe"] = b"2" + self.tdb[b"bla"] = b"25" self.assertEquals(2, len(list(self.tdb))) self.tdb.clear() self.assertEquals(0, len(list(self.tdb))) def test_repack(self): - self.tdb["foo"] = "abc" - self.tdb["bar"] = "def" - del self.tdb["foo"] + self.tdb[b"foo"] = b"abc" + self.tdb[b"bar"] = b"def" + del self.tdb[b"foo"] self.tdb.repack() def test_seqnum(self): @@ -164,12 +194,110 @@ class SimpleTdbTests(TestCase): def test_len(self): self.assertEquals(0, len(list(self.tdb))) - self.tdb["entry"] = "value" + self.tdb[b"entry"] = b"value" self.assertEquals(1, len(list(self.tdb))) - def test_add_flags(self): - self.tdb.add_flags(tdb.NOMMAP) - self.tdb.remove_flags(tdb.NOMMAP) + +class TdbTextTests(TestCase): + + def setUp(self): + super(TdbTextTests, self).setUp() + self.tdb = tdb.Tdb(tempfile.mkstemp()[1], 0, tdb.DEFAULT, + os.O_CREAT|os.O_RDWR) + self.assertNotEqual(None, self.tdb) + + def test_repr(self): + self.assertTrue(repr(self.tdb).startswith("Tdb('")) + + def test_store(self): + self.tdb.text.store("bar", "bla") + self.assertEquals("bla", self.tdb.text.get("bar")) + + def test_getitem(self): + self.tdb.text["bar"] = "foo" + self.tdb.reopen() + self.assertEquals("foo", self.tdb.text["bar"]) + + def test_delete(self): + self.tdb.text["bar"] = "foo" + del self.tdb.text["bar"] + self.assertRaises(KeyError, lambda: self.tdb.text["bar"]) + + def test_contains(self): + self.tdb.text["bla"] = "bloe" + self.assertTrue("bla" in self.tdb.text) + self.assertFalse("qwertyuiop" in self.tdb.text) + if sys.version_info < (3, 0): + self.assertTrue(self.tdb.text.has_key("bla")) + self.assertFalse(self.tdb.text.has_key("qwertyuiop")) + + def test_keyerror(self): + self.assertRaises(KeyError, lambda: self.tdb.text["bla"]) + + def test_iterator(self): + self.tdb.text["bla"] = "1" + self.tdb.text["brainslug"] = "2" + l = list(self.tdb.text) + l.sort() + self.assertEquals(["bla", "brainslug"], l) + + def test_transaction_cancel(self): + self.tdb.text["bloe"] = "2" + self.tdb.transaction_start() + self.tdb.text["bloe"] = "1" + self.tdb.transaction_cancel() + self.assertEquals("2", self.tdb.text["bloe"]) + + def test_transaction_commit(self): + self.tdb.text["bloe"] = "2" + self.tdb.transaction_start() + self.tdb.text["bloe"] = "1" + self.tdb.transaction_commit() + self.assertEquals("1", self.tdb.text["bloe"]) + + def test_transaction_prepare_commit(self): + self.tdb.text["bloe"] = "2" + self.tdb.transaction_start() + self.tdb.text["bloe"] = "1" + self.tdb.transaction_prepare_commit() + self.tdb.transaction_commit() + self.assertEquals("1", self.tdb.text["bloe"]) + + def test_iterkeys(self): + self.tdb.text["bloe"] = "2" + self.tdb.text["bla"] = "25" + if sys.version_info >= (3, 0): + i = self.tdb.text.keys() + else: + i = self.tdb.text.iterkeys() + self.assertEquals(set(["bloe", "bla"]), set([next(i), next(i)])) + + def test_clear(self): + self.tdb.text["bloe"] = "2" + self.tdb.text["bla"] = "25" + self.assertEquals(2, len(list(self.tdb))) + self.tdb.clear() + self.assertEquals(0, len(list(self.tdb))) + + def test_repack(self): + self.tdb.text["foo"] = "abc" + self.tdb.text["bar"] = "def" + del self.tdb.text["foo"] + self.tdb.repack() + + def test_len(self): + self.assertEquals(0, len(list(self.tdb.text))) + self.tdb.text["entry"] = "value" + self.assertEquals(1, len(list(self.tdb.text))) + + def test_text_and_binary(self): + text = u'\xfa\u0148\xef\xe7\xf8\xf0\xea' + bytestr = text.encode('utf-8') + self.tdb[b"entry"] = bytestr + self.tdb.text[u"entry2"] = text + self.assertEquals(self.tdb.text["entry"], text) + self.assertEquals(self.tdb[b"entry2"], bytestr) + assert self.tdb.text.raw == self.tdb class VersionTests(TestCase): diff --git a/test/run-allrecord-traverse-deadlock.c b/test/run-allrecord-traverse-deadlock.c new file mode 100644 index 0000000..2c58206 --- /dev/null +++ b/test/run-allrecord-traverse-deadlock.c @@ -0,0 +1,203 @@ +#include "../common/tdb_private.h" +#include "../common/io.c" +#include "../common/tdb.c" +#include "../common/lock.c" +#include "../common/freelist.c" +#include "../common/traverse.c" +#include "../common/transaction.c" +#include "../common/error.c" +#include "../common/open.c" +#include "../common/check.c" +#include "../common/hash.c" +#include "../common/mutex.c" +#include "tap-interface.h" +#include <stdlib.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <stdarg.h> +#include "logging.h" + +static void do_allrecord_lock(const char *name, int tdb_flags, int up, + int down) +{ + struct tdb_context *tdb; + int ret; + ssize_t nread, nwritten; + char c = 0; + + tdb = tdb_open_ex(name, 3, tdb_flags, + O_RDWR|O_CREAT, 0755, &taplogctx, NULL); + ok(tdb, "tdb_open_ex should succeed"); + + ret = tdb_lockall(tdb); + ok(ret == 0, "tdb_lockall should succeed"); + + nwritten = write(up, &c, sizeof(c)); + ok(nwritten == sizeof(c), "write should succeed"); + + nread = read(down, &c, sizeof(c)); + ok(nread == sizeof(c), "read should succeed"); + + ret = tdb_traverse(tdb, NULL, NULL); + ok(ret == -1, "do_allrecord_lock: traverse should fail"); + + nwritten = write(up, &c, sizeof(c)); + ok(nwritten == sizeof(c), "write should succeed"); + + exit(0); +} + +static void do_traverse(const char *name, int tdb_flags, int up, int down) +{ + struct tdb_context *tdb; + int ret; + ssize_t nread, nwritten; + char c = 0; + + tdb = tdb_open_ex(name, 3, tdb_flags, + O_RDWR|O_CREAT, 0755, &taplogctx, NULL); + ok(tdb, "tdb_open_ex should succeed"); + + ret = tdb_traverse(tdb, NULL, NULL); + ok(ret == 1, "do_traverse: tdb_traverse should return 1 record"); + + nwritten = write(up, &c, sizeof(c)); + ok(nwritten == sizeof(c), "write should succeed"); + + nread = read(down, &c, sizeof(c)); + ok(nread == sizeof(c), "read should succeed"); + + exit(0); +} + +/* + * Process 1: get the allrecord_lock on a tdb. + * Process 2: start a traverse, this will stall waiting for the + * first chainlock: That is taken by the allrecord_lock + * Process 1: start a traverse: This will get EDEADLK in trying to + * get the TRANSACTION_LOCK. It will deadlock for mutexes, + * which don't have built-in deadlock detection. + */ + +static int do_tests(const char *name, int tdb_flags) +{ + struct tdb_context *tdb; + int ret; + pid_t traverse_child, allrecord_child; + int traverse_down[2]; + int traverse_up[2]; + int allrecord_down[2]; + int allrecord_up[2]; + char c; + ssize_t nread, nwritten; + TDB_DATA key, data; + + key.dsize = strlen("hi"); + key.dptr = discard_const_p(uint8_t, "hi"); + data.dsize = strlen("world"); + data.dptr = discard_const_p(uint8_t, "world"); + + tdb = tdb_open_ex(name, 3, tdb_flags, + O_RDWR|O_CREAT, 0755, &taplogctx, NULL); + ok(tdb, "tdb_open_ex should succeed"); + + ret = tdb_store(tdb, key, data, TDB_INSERT); + ok(ret == 0, "tdb_store should succeed"); + + ret = pipe(traverse_down); + ok(ret == 0, "pipe should succeed"); + + ret = pipe(traverse_up); + ok(ret == 0, "pipe should succeed"); + + ret = pipe(allrecord_down); + ok(ret == 0, "pipe should succeed"); + + ret = pipe(allrecord_up); + ok(ret == 0, "pipe should succeed"); + + allrecord_child = fork(); + ok(allrecord_child != -1, "fork should succeed"); + + if (allrecord_child == 0) { + tdb_close(tdb); + close(traverse_up[0]); + close(traverse_up[1]); + close(traverse_down[0]); + close(traverse_down[1]); + close(allrecord_up[0]); + close(allrecord_down[1]); + do_allrecord_lock(name, tdb_flags, + allrecord_up[1], allrecord_down[0]); + exit(0); + } + close(allrecord_up[1]); + close(allrecord_down[0]); + + nread = read(allrecord_up[0], &c, sizeof(c)); + ok(nread == sizeof(c), "read should succeed"); + + traverse_child = fork(); + ok(traverse_child != -1, "fork should succeed"); + + if (traverse_child == 0) { + tdb_close(tdb); + close(traverse_up[0]); + close(traverse_down[1]); + close(allrecord_up[0]); + close(allrecord_down[1]); + do_traverse(name, tdb_flags, + traverse_up[1], traverse_down[0]); + exit(0); + } + close(traverse_up[1]); + close(traverse_down[0]); + + poll(NULL, 0, 1000); + + nwritten = write(allrecord_down[1], &c, sizeof(c)); + ok(nwritten == sizeof(c), "write should succeed"); + + nread = read(traverse_up[0], &c, sizeof(c)); + ok(nread == sizeof(c), "read should succeed"); + + nwritten = write(traverse_down[1], &c, sizeof(c)); + ok(nwritten == sizeof(c), "write should succeed"); + + nread = read(allrecord_up[0], &c, sizeof(c)); + ok(nread == sizeof(c), "ret should succeed"); + + close(traverse_up[0]); + close(traverse_down[1]); + close(allrecord_up[0]); + close(allrecord_down[1]); + diag("%s tests done", name); + return exit_status(); +} + +int main(int argc, char *argv[]) +{ + int ret; + bool mutex_support; + + mutex_support = tdb_runtime_check_for_robust_mutexes(); + + ret = do_tests("marklock-deadlock-fcntl.tdb", + TDB_CLEAR_IF_FIRST | + TDB_INCOMPATIBLE_HASH); + ok(ret == 0, "marklock-deadlock-fcntl.tdb tests should succeed"); + + if (!mutex_support) { + skip(1, "No robust mutex support, " + "skipping marklock-deadlock-mutex.tdb tests"); + return exit_status(); + } + + ret = do_tests("marklock-deadlock-mutex.tdb", + TDB_CLEAR_IF_FIRST | + TDB_MUTEX_LOCKING | + TDB_INCOMPATIBLE_HASH); + ok(ret == 0, "marklock-deadlock-mutex.tdb tests should succeed"); + + return exit_status(); +} diff --git a/tools/tdbrestore.c b/tools/tdbrestore.c index f65b36f..81c986c 100644 --- a/tools/tdbrestore.c +++ b/tools/tdbrestore.c @@ -17,8 +17,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#include <assert.h> #include "replace.h" +#include <assert.h> #include "system/locale.h" #include "system/time.h" #include "system/filesys.h" @@ -1,7 +1,7 @@ #!/usr/bin/env python APPNAME = 'tdb' -VERSION = '1.3.5' +VERSION = '1.3.7' blddir = 'bin' @@ -41,6 +41,7 @@ tdb1_unit_tests = [ 'run-wronghash-fail', 'run-zero-append', 'run-marklock-deadlock', + 'run-allrecord-traverse-deadlock', 'run-mutex-openflags2', 'run-mutex-trylock', 'run-mutex-allrecord-bench', @@ -95,8 +96,7 @@ def configure(conf): if not conf.env.disable_python: # also disable if we don't have the python libs installed - conf.find_program('python', var='PYTHON') - conf.check_tool('python') + conf.SAMBA_CHECK_PYTHON(mandatory=False) conf.check_python_version((2,4,2)) conf.SAMBA_CHECK_PYTHON_HEADERS(mandatory=False) if not conf.env.HAVE_PYTHON_H: @@ -179,12 +179,20 @@ def build(bld): includes='include', install=False) if not bld.CONFIG_SET('USING_SYSTEM_PYTDB'): - bld.SAMBA_PYTHON('pytdb', - 'pytdb.c', - deps='tdb', - enabled=not bld.env.disable_python, - realname='tdb.so', - cflags='-DPACKAGE_VERSION=\"%s\"' % VERSION) + for env in bld.gen_python_environments(['PKGCONFIGDIR']): + bld.SAMBA_PYTHON('pytdb', + 'pytdb.c', + deps='tdb', + enabled=not bld.env.disable_python, + realname='tdb.so', + cflags='-DPACKAGE_VERSION=\"%s\"' % VERSION) + + for env in bld.gen_python_environments(['PKGCONFIGDIR']): + bld.SAMBA_SCRIPT('_tdb_text.py', + pattern='_tdb_text.py', + installdir='python') + + bld.INSTALL_FILES('${PYTHONARCHDIR}', '_tdb_text.py') def testonly(ctx): '''run tdb testsuite''' @@ -224,7 +232,10 @@ def testonly(ctx): print("testsuite returned %d" % ret) if ret != 0: ecode = ret - sys.exit(ecode) + + pyret = samba_utils.RUN_PYTHON_TESTS(['python/tests/simple.py']) + print("python testsuite returned %d" % pyret) + sys.exit(ecode or pyret) # WAF doesn't build the unit tests for this, maybe because they don't link with tdb? # This forces it |