aboutsummaryrefslogtreecommitdiffstats
path: root/makewrappers
AgeCommit message (Collapse)Author
2021-05-18makewrappers: Handle parameters marked as nonnullPhilip Lorenz
Commit 60e25a36558f1f07dcce1a044fe976b475bec42b started dereferencing the "path" parameter which for some functions is annotated with the "nonnull" attribute. While the commit explicitly checks for NULL pointers before dereferencing it, GCC (at optimization level 1 and above) removes the check due to the "nonnull" attribute being set for some parameters in the glibc headers (e.g. statx()). However, the statx() man page explicitly allows calling with NULL pointers (in which case the EFAULT is returned) and this behaviour is used in the wild (e.g. in Rust) to determine whether the statx() system call is supported. Disabling the optimization is not possible ([1]) so prevent the compiler optimization by referencing the parameter in a noop inline assembly instruction instead. [1] https://gcc.gnu.org/bugzilla/show_bug.cgi?id=100404 Signed-off-by: Philip Lorenz <philip@bithub.de> Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
2021-02-15makewrappers: Fix glibc 2.33 fstatat usage issuesRichard Purdie
In glibc 2.33 it makes calls like: fstatat64 (pathfd, "", &st, AT_EMPTY_PATH); where pathfd may be a symlink. This interacts badly with pseudo_root_path() since the empty path is replaced with a pathname from the open fd but AT_SYMLINK_NOFOLLOW is not set, hence the link is resolved and pseudo throws an abort() due to inode mismatch. Where the path is empty, an fd is passed and AT_EMPTY_PATH is set, we can imply that AT_SYMLINK_NOFOLLOW is also effectly set. Adjust the wrapper functions to ensure this, allowing the functions to behave correctly in the AT_EMPTY_PATH case. Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
2021-01-09makewrappers: support architecture-overrides in wrapper modifiersRoss Burton
Pseudo allows wrappers to define special comments in the wrapper lists to pass extra arguments such as version=GLIBC_2.3 to control which symbol version to search for. However, these arguments can be architecture-specific. When parsing the arguments, check for flags that end in the architecture name (as returned by platform.machine()) and use those values instead. Signed-off-by: Ross Burton <ross.burton@arm.com> Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
2021-01-09makewrappers: fix Python 2 hangoverRoss Burton
An except statement was still using Python 2 syntax so caused SyntaxErrors if the exception was raised. Signed-off-by: Ross Burton <ross.burton@arm.com> Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
2020-10-08pseudo: Add support for ignoring pathsRichard Purdie
Currently, pseudo considers any path accessed whist its running to be a valid entry to track in its database. The way OpenEmbedded uses pseudo, there are paths we care about accesses to from a pseudo perspective and paths which we simply don't care about. This patch adds a PSEUDO_IGNORE_PATHS environment variable which is a comma separated list of path prefixes to ignore accesses to. To do this, we add some functions which can check a path argument or a file descriptor argument and use these in the pseudo wrappers where path or fd arguments are present. Where paths are being ignored, we skip straight to the underlying real function. Psuedo needs to keep track of the open fd mappings to files so we still need to allow those cases into the pseudo_op function. Specficially this means OP_CLOSE, OP_OPEN, OP_DUP and OP_CHDIR. Apart from OP_OPEN which could call the server, the other operations are client side only so passed through. We 'tag' the functions using these operations so that the path ignore code isn't triggered. For OP_OPEN we exit early and skip the server op. We also have a catch all in client_op to ensure any operatings we didn't manage to skip early still get skipped correctly. OP_CHROOT is a special case. Where ignored path prefixes are used as a chroot, for the lifetime of the chroot, the path is effectively dropped from the PSEUDO_IGNORE_PATHS list. Whilst slightly counter intuaitive, this turned out to be the most effective way to do things due to commands like useradd and their use of chroots. For sqlite3 and appropriate path filtering in OE, this took the database from 45,000 entries to about 180. For dbus this was 88,000 down to 760. Given the number of client to server trips these numbers of paths involves, the win is seemingly worthwhile. Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
2020-06-26maketables/wrappers: use Python 3Alexander Kanavin
Changelog indicates they should be compatible. Upstream-Status: Pending Signed-off-by: Alexander Kanavin <alex.kanavin@gmail.com> Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
2019-05-15Add SPDX-License-Identifier: LGPL-2.1-only to filesRichard Purdie
This adds SPDX license headers to all source files in pseudo so license identification models current best practise. Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
2018-04-13Fix symlink following errorsSeebs
openat() was passing its flags unaltered to pseudo_root_path(), which assumes that a flags argument other than 0 means "don't follow symlinks in last path component". This is completely wrong, and I have no idea how it survived this long unnoticed. Now, if a plain flags variable is set and not overruled by a comment like /* flags=... */, it's masked with AT_SYMLINK_NOFOLLOW, as there are other values fstatat() and friends can take, and the openat() flags are just overridden with 0. (The only meaningful case would be O_NOFOLLOW, but O_NOFOLLOW instructs us to *fail* in the open if the path is a symlink, so we don't care.) Signed-off-by: Seebs <seebs@seebs.net>
2016-12-12Python scripts are now compatible with both version of python, 2 and 3.Seebs
Helped-by: Damien Riegel <damien.riegel@savoirfairelinux.com> Helped-by: Alexandre Leblanc <alexandre.leblanc@savoirfairelinux.com> Signed-off-by: Gaël PORTAY <gael.portay@savoirfairelinux.com> Signed-off-by: Seebs <seebs@seebs.net>
2016-12-12Python 3 says:Seebs
File "./makewrappers", line 459 print port ^ SyntaxError: Missing parentheses in call to 'print' Signed-off-by: Gaël PORTAY <gael.portay@savoirfairelinux.com> Signed-off-by: Seebs <seebs@seebs.net> --- maketables | 12 ++++++------ makewrappers | 32 ++++++++++++++++---------------- templatefile.py | 8 ++++---- 3 files changed, 26 insertions(+), 26 deletions(-) diff --git a/maketables b/maketables index b32312e..0726485 100755 --- a/maketables +++ b/maketables @@ -73,7 +73,7 @@ class DataType: for col in columns: indexed = False if col.startswith("FLAGS"): - print "Flags: set for %s" % self.name + print("Flags: set for %s" % self.name) self.flags = True continue if col.startswith("INDEXED "): @@ -248,7 +248,7 @@ def main(): template_file.emit('header') templates.append(template_file) except IOError: - print "Invalid or malformed template %s. Aborting." % path + print("Invalid or malformed template %s. Aborting." % path) exit(1) for filename in sys.argv[1:]: @@ -256,15 +256,15 @@ def main(): sys.stdout.write("%s: " % filename) datatype = DataType(filename) datatypes.append(datatype) - print datatype.__repr__() - print "" + print(datatype.__repr__()) + print("") - print "Writing datatypes...", + print("Writing datatypes...") for datatype in datatypes: # populate various tables and files with each datatype for template_file in templates: template_file.emit('body', datatype) - print "done. Cleaning up." + print("done. Cleaning up.") for template_file in templates: # clean up files diff --git a/makewrappers b/makewrappers index 303e2cc..bac856b 100755 --- a/makewrappers +++ b/makewrappers @@ -456,7 +456,7 @@ additional ports to include. self.name = port self.subports = [] self.preports = [] - print port + print(port) if os.path.exists(self.portfile("pseudo_wrappers.c")): self.wrappers = self.portfile("pseudo_wrappers.c") @@ -504,17 +504,17 @@ additional ports to include. prefuncs = pre.functions() for name in prefuncs.keys(): if name in mergedfuncs: - print "Warning: %s from %s overriding %s" % (name, pre.name, mergedfuncs[name].port) + print("Warning: %s from %s overriding %s" % (name, pre.name, mergedfuncs[name].port)) mergedfuncs[name] = prefuncs[name] for name in self.funcs.keys(): if name in mergedfuncs: - print "Warning: %s from %s overriding %s" % (name, self.name, mergedfuncs[name].port) + print("Warning: %s from %s overriding %s" % (name, self.name, mergedfuncs[name].port)) mergedfuncs[name] = self.funcs[name] for sub in self.subports: subfuncs = sub.functions() for name in subfuncs.keys(): if name in mergedfuncs: - print "Warning: %s from %s overriding %s" % (name, sub.name, mergedfuncs[name].port) + print("Warning: %s from %s overriding %s" % (name, sub.name, mergedfuncs[name].port)) mergedfuncs[name] = subfuncs[name] return mergedfuncs @@ -576,11 +576,11 @@ def process_wrapfuncs(port): func.directory = directory funcs[func.name] = func sys.stdout.write(".") - except Exception, e: - print "Parsing failed:", e + except Exception(e): + print("Parsing failed:", e) exit(1) funclist.close() - print "" + print("") return funcs def main(argv): @@ -599,35 +599,35 @@ def main(argv): for path in glob.glob('templates/*'): try: - print "Considering template: " + path + print("Considering template: " + path) source = TemplateFile(path) if source.name.endswith('.c') or source.name.endswith('.h'): source.emit('copyright') source.emit('header') sources.append(source) except IOError: - print "Invalid or malformed template %s. Aborting." % path + print("Invalid or malformed template %s. Aborting." % path) exit(1) try: port = Port('common', sources) except KeyError: - print "Unknown uname -s result: '%s'." % uname_s - print "Known system types are:" - print "%-20s %-10s %s" % ("uname -s", "port name", "description") + print("Unknown uname -s result: '%s'." % uname_s) + print("Known system types are:") + print("%-20s %-10s %s" % ("uname -s", "port name", "description")) for key in host_ports: - print "%-20s %-10s %s" % (key, host_ports[key], - host_descrs[host_ports[key]]) + print("%-20s %-10s %s" % (key, host_ports[key], + host_descrs[host_ports[key]])) # the per-function stuff - print "Writing functions...", + print("Writing functions...") all_funcs = port.functions() for name in sorted(all_funcs.keys()): # populate various tables and files with each function for source in sources: source.emit('body', all_funcs[name]) - print "done. Cleaning up." + print("done. Cleaning up.") for source in sources: # clean up files diff --git a/templatefile.py b/templatefile.py index 2789b22..abf9a2c 100644 --- a/templatefile.py +++ b/templatefile.py @@ -79,13 +79,13 @@ class TemplateFile: return path = Template(self.path).safe_substitute(item) if os.path.exists(path): - # print "We don't overwrite existing files." + # print("We don't overwrite existing files.") return self.file = open(path, 'w') if not self.file: - print "Couldn't open '%s' (expanded from %s), " \ + print("Couldn't open '%s' (expanded from %s), " \ "not emitting '%s'." % \ - (path, self.path, template) + (path, self.path, template)) return def emit(self, template, item=None): @@ -103,7 +103,7 @@ class TemplateFile: self.file.write(templ.safe_substitute(item)) self.file.write("\n") else: - print "Warning: Unknown template '%s'." % template + print("Warning: Unknown template '%s'." % template) if self.file_per_item: if self.file: -- 2.10.1
2016-12-12Python 3 reports:Seebs
File "./makewrappers", line 327 return """/* This function is not called if pseudo is configured --enable-force-async */ ^ TabError: inconsistent use of tabs and spaces in indentation Signed-off-by: Gaël PORTAY <gael.portay@savoirfairelinux.com> Signed-off-by: seebs <seebs@seebs.net> --- makewrappers | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/makewrappers b/makewrappers index e9191ed..303e2cc 100755 --- a/makewrappers +++ b/makewrappers @@ -324,7 +324,7 @@ class Function: def maybe_async_skip(self): if self.async_skip: - return """/* This function is not called if pseudo is configured --enable-force-async */ + return """/* This function is not called if pseudo is configured --enable-force-async */ #ifdef PSEUDO_FORCE_ASYNC if (!pseudo_allow_fsync) { PROFILE_DONE; @@ -333,7 +333,7 @@ class Function: #endif """ % self.async_skip else: - return "" + return "" def comment(self): """declare self (in a comment)""" @@ -393,11 +393,11 @@ class Function: def rc_format(self): """the format string to use for the return value""" - return typedata.get(self.type, { 'format': '[%s]', 'value': '"' + self.type + '"' })['format'] + return typedata.get(self.type, { 'format': '[%s]', 'value': '"' + self.type + '"' })['format'] def rc_value(self): """the value to pass for the format string for the return value""" - return typedata.get(self.type, { 'format': '[%s]', 'value': '"' + self.type + '"' })['value'] + return typedata.get(self.type, { 'format': '[%s]', 'value': '"' + self.type + '"' })['value'] def rc_decl(self): """declare rc (if needed)""" @@ -456,7 +456,7 @@ additional ports to include. self.name = port self.subports = [] self.preports = [] - print port + print port if os.path.exists(self.portfile("pseudo_wrappers.c")): self.wrappers = self.portfile("pseudo_wrappers.c") @@ -522,11 +522,11 @@ additional ports to include. return '#define PSEUDO_PORT_%s 1' % string.upper(self.name).replace('/', '_') def portdeps(self): - deps = [] - if self.wrappers: - deps.append(self.wrappers) - if self.portdef_file: - deps.append(self.portdef_file) + deps = [] + if self.wrappers: + deps.append(self.wrappers) + if self.portdef_file: + deps.append(self.portdef_file) if deps: return 'pseudo_wrappers.o: %s' % ' '.join(deps) else: @@ -590,7 +590,7 @@ def main(argv): for arg in argv: name, value = arg.split('=') - os.environ["port_" + name] = value + os.environ["port_" + name] = value # error checking helpfully provided by the exception handler copyright_file = open('guts/COPYRIGHT') @@ -599,9 +599,9 @@ def main(argv): for path in glob.glob('templates/*'): try: - print "Considering template: " + path + print "Considering template: " + path source = TemplateFile(path) - if source.name.endswith('.c') or source.name.endswith('.h'): + if source.name.endswith('.c') or source.name.endswith('.h'): source.emit('copyright') source.emit('header') sources.append(source) -- 2.10.1
2015-09-04Add return value printing to wrappersPeter Seebach
I never did this because how could you do it generically, then a friend who is better at Python gave me an idea for a way to do it, and now wrapper debugging prints return values, not just errno values, in most-to-all cases. Signed-off-by: Peter Seebach <peter.seebach@windriver.com>
2015-08-20Drop the allocation in pseudo_fix_path/pseudo_root_path/etc.Peter Seebach
Instead of allocating (and then freeing) these paths all the time, use a rotating selection of buffers of fixed but probably large enough size (the same size that would have been the maximum anyway in general). With the exception of fts_open, there's no likely way to end up needing more than two or three such paths at a time. fts_open dups the paths since it could have a large number and need them for a while. This dramatically reduces (in principle) the amount of allocation and especially reallocation going on. Signed-off-by: Peter Seebach <peter.seebach@windriver.com>
2015-08-20Initial profiling implementation.Peter Seebach
A partially-implemented profiler for client time, which basically just inserts (optional) gettimeofday calls in various places and stashes data in a flat file containing one data block per pid. Signed-off-by: Peter Seebach <peter.seebach@windriver.com>
2014-05-27Honor umaskPeter Seebach
We used to rely on filesystem operations to apply the umask when appropriate, but when we started masking out 022, that stopped working. Start watching umask.
2014-04-24Make configure handle xattr guessing (or specifying)Peter Seebach
Clean-up: Allow specification of environment hints for subports scripts, such as whether xattr support is available. Also make configure guess at a bit width if none is specified. Signed-off-by: Peter Seebach <peter.seebach@windriver.com>
2014-04-21Automatic dependency checking for wrappersPeter Seebach
Ports can provide pseudo_wrappers.c or portdefs.h, and individual functions have implementations. These dependencies aren't known until post-configure. Make the Makefile include two sub-Makefiles which can be updated by makewrappers. Signed-off-by: Peter Seebach <peter.seebach@windriver.com>
2013-02-26PSEUDO_ALLOW_FSYNC: Allow fsync()pseudo-1.5PSEUDO_1_5Peter Seebach
Some filesystems have buggy semantics where stat(2) will return incorrect sizes for files for a while after some changes, sometimes, unless they've been fsync'd. We still want to disable fsync most of the time, but enabling it for specific programs can be useful. Signed-off-by: Peter Seebach <peter.seebach@windriver.com>
2013-02-17Darwin fixes for fsync changes, uninitialized variablePeter Seebach
Darwin's off_t is a 64-bit type, so there's no off64_t. Also, there's an uninitialized variable usage in unlinkat which LLVM catches. Signed-off-by: Peter Seebach <peter.seebach@windriver.com>
2013-02-16allow pseudo to force asynchronous behaviorPeter Seebach
The openembedded build, at least with RPM or SMART, is heavily affected by the cost of calling fsync or fdatasync on package databases all the time. Gosh, wouldn't it be nice if we could suppress that without making dozens of highly intrusive and risky changes into RPM, various database packages, and so on? Yes, yes it would. If only there were a program which could intercept system calls and change their behavior! Enter --enable-force-async. There are now wrappers for fsync, fdatasync, and a few related functions. If --enable-force-async is set, these wrappers instantly return 0, even if PSEUDO_DISABLED is set. And with any luck, bitbake will now perform a bit better. Credit for this insight goes to Richard Purdie. I've reimplemented this to add the configure option, and make the fsync suppression work even when PSEUDO_DISABLED is set.
2012-12-12add linkat() implementationPeter Seebach
We never had an implementation for linkat() because no one used it; now someone uses it. link() is now implemented on top of linkat(). Note the abnormal AT_SYMLINK_FOLLOW (as opposed to _NOFOLLOW) flag.
2012-02-06fix spaces/tabs in python againPeter Seebach
2012-02-06Fix *at() function interface holesPeter Seebach
1. Fix *at() where dirfd is obtained through dirfd(DIR *). The dirfd(DIR *) interface allows you to get the fd for a DIR *, meaning you can use it with openat(), meaning you can need its path. This causes a segfault. Also fixed the base_path code not to segfault in that case, but first fix the underlying problem. 2. Implement renameat() After three long years, someone tried to use this. This was impossibly hard back when pseudo was written, because there was only one dirfd provided for. Thing is, now, the canonicalization happens in wrapfuncs, so a small tweak to makewrappers to recognize that oldpath should use olddirfd if it exists is enough to get us fully canonicalized paths when needed.
2011-06-09Fix realpath(name, NULL) when PSEUDO_DISABLED=1Peter Seebach
On some Linux systems, dlsym("realpath", RTLD_NEXT) prefers for reasons of its own to give a symbol that is also known as old_realpath, which fails and yields EINVAL when called with a null pointer as the second argument. This can be avoided, on some systems, by using dlvsym() to request the GLIBC_2.3 version of the symbol. The wrapper logic is enhanced to allow for specifying versions, although this currently only works for Linux (Darwin has no dlvsym, apparently?). The test case is a trivial program which calls realpath(name, NULL) run with PSEUDO_DISABLED=1.
2011-04-04Whitespace changes.Peter Seebach
2011-03-25Merge in ports workPeter Seebach
This is a spiffied-up rebase of a bunch of intermediate changes, presented as a whole because it is, surprisingly, less confusing that way. The basic idea is to separate the guts code into categories ranging from generic stuff that can be the same everywhere and specific variants. The big scary one is the Darwin support, which actually seems to run okay on 64-bit OS X 10.6. (No other variants were tested.) The other example given is support for the old clone() syscall on RHEL 4, which affects some wrlinux use cases. There's a few minor cleanup bits here, such as a function with inconsistent calling conventions, but nothing really exciting.
2010-12-07This is a merge of several commits from a tree which turned out toPeter Seebach
be out of sync in a very inconvenient way. Changes include: * Some whitespace fixes, also move the pseudo_variables definition into pseudo_util.c since it's not used anywhere else. * Further improvements in the fork() support: We now recognize both positive and negative forms of PSEUDO_DISABLED, so we can distinguish between "it was removed from the environment by env -i" (restore the old value) and "it was intentionally turned off" (the new value wins). * clone(2) support. This is a little primitive, and programs might still fail horribly due to clone's semantics, but at least it's there and passes easy test cases. Plus a big patch from Mark Hatle: Cleanup fork/clone and PSEUDO_DISABLED guts/fork.c: * cleanup function and make it more robust * be sure to call pseudo_setupenv prior to pseudo_client_reset to match exec behavior pseudo_wrappers.c: * fix mismatched type in execl_to_v call via typecast * Simplify fork call via single call to wrap_fork() * be sure to save pseudo_disabled * be sure to call pseudo_setupenv prior to pseudo_client_reset to match exec behavior tests: * Add a test of whether pseudo can be disabled/enabled on a fork. Signed-off-by: Mark Hatle <mark.hatle@windriver.com>
2010-11-18Messing with makewrappers: Move templating code out (for planned usePeter Seebach
in an incoming "maketables".)
2010-10-25Clean up makewrappers a bit.Peter Seebach
2010-10-12Remove tabs from makewrappers.Peter Seebach
2010-10-12Was missing copyright in new makewrappers.Peter Seebach
2010-10-11Major change: Replace the shell-based makewrappers with a PythonPeter Seebach
one. There's a long story here, but to abbreviate it: The shell script was annoying at best to maintain and starting to show signs of not really being the right tool for the job. For various reasons, we have some other Python stuff in our build system, so we picked Python as the language we were already using for other stuff. We think this works with anything reasonably recent (around Python 2.4 through 2.6). There's a little bit of cleanup, also, of the wrapper templates.
2010-08-26Almost had it! The problem is that because the save ofseebs
pseudo_saved_sigmask occurred outside of the check against antimagic, calls to wrapped functions made by the internals of a wrapper resulted on overwriting pseudo_saved_sigmask, so we restored the already-blocked sigmask. This could result in programs getting their sigmask permanently changed to block various signals, most crucially SIGCHLD. We now stash the value immediately before calling a wrapper, and never stash it if we're not calling a wrapper. Now the anti-magic stuff happening inside wrappers isn't trashing the signal mask. YAY!
2010-08-25Initial attempt at fixing problems with SIGCHLD being blockedseebs
in processes started under pseudo.
2010-08-11Enable execl, execle, execlp, execv, and execvp wrappersMark Hatle
We wrap all of the execs so that we can ensure the environment is properly configured prior to the exec running. handle ... for the new execl* wrappers Add a test for the new execl* ... handling.
2010-04-30Miscellaneous cleanup.Peter Seebach
Address a couple of compiler warnings, add a couple of signals to the list of caught signals, etcetera.
2010-04-26Avoid signalling during mutex operations.Peter Seebach
remake(1) can try to close a file from a signal handler. Since any entrance to a pseudo-emulated function requires the mutex, this can result in a deadlock. Solution: Suspend SIGCHLD (and a couple of other signals likely to result in filesystem operations) for the duration of the lock.
2010-04-26Reduce race condition for unlinks.Peter Seebach
If multiple clients are active at once, the following could occur: * Client #1 unlinks file A * Client #2 creates file B, which reuses A's inode * Client #2 sends request to server * Client #1 sends request to server * Processing client #2's request creates a mismatch warning for file A/B. * Processing client #1's request creates a mismatch warning too. Note that this can happen even if Client #2 sends its request later, as there's no intrinsic guarantee of the order in which requests are processed; any SINGLE client is presumably executing operations in order, but multiple clients aren't. Fixing this in rmdir, unlink, and rename.
2010-04-05Shuffle some code around.Peter Seebach
Migrate the stable part of the wrapper code (not machine-generated) out of makewrappers, to make it easier to maintain.
2010-03-30Fix copyright information.Peter Seebach
Corporate policy is that each module should have a copyright notice.
2010-03-29Add password/group call emulation.Peter Seebach
This is a first pass at handling password/group calls, allowing the use of custom password/group files. In particular, when chroot()ed to a particular directory, pseudo picks files in that directory by default, to improve support for the typical use case where pseudo uses chroot() only to jump into a virtual target filesystem.
2010-03-26Updates: Enable additional warnings, fix a number of things.Peter Seebach
None of them seem to have been genuine problems, but it's prettier now, and some were questionable.
2010-03-24Prep for chroot handling:Peter Seebach
* Improve makewrappers handling of function pointer arguments. * Regenerate wrappers when makewrappers is touched. * Move path resolution from pseudo_client_op into wrapper functions. * Eliminate dependency on PATH_MAX. * Related cleanup, such as tracking CWD better, and using the tracked value for getcwd().
2010-03-16initial public releasePeter Seebach