diff options
-rw-r--r-- | ChangeLog.txt | 2 | ||||
-rw-r--r-- | Makefile.in | 3 | ||||
-rwxr-xr-x | makewrappers | 914 | ||||
-rw-r--r-- | pseudo_wrappers.c | 24 | ||||
-rw-r--r-- | templates/guts | 15 | ||||
-rw-r--r-- | templates/wrapfuncs.c | 78 | ||||
-rw-r--r-- | templates/wrapfuncs.h | 10 | ||||
-rw-r--r-- | templates/wrapper_table | 20 |
8 files changed, 600 insertions, 466 deletions
diff --git a/ChangeLog.txt b/ChangeLog.txt index 788901c..d477da2 100644 --- a/ChangeLog.txt +++ b/ChangeLog.txt @@ -1,5 +1,7 @@ 2010-10-11: * (seebs) do the other *xattr() wrappers. + * (seebs) Replace makewrappers with Python implementation and some + template files. 2010-10-06: * (mhatle) Add the fsetxattr wrapper to return ENOTSUP diff --git a/Makefile.in b/Makefile.in index 2bf8197..c867978 100644 --- a/Makefile.in +++ b/Makefile.in @@ -56,6 +56,7 @@ PSEUDODB=$(BIN)/pseudodb PSEUDOLOG=$(BIN)/pseudolog LIBPSEUDO=$(LIB)/libpseudo.so +TEMPLATES=templates/guts templates/wrapfuncs.c templates/wrapfuncs.h templates/wrapper_table all: $(LIBPSEUDO) $(PSEUDO) $(PSEUDODB) $(PSEUDOLOG) @@ -114,7 +115,7 @@ pseudo_client.o: pseudo_client.h pseudo_server.o: pseudo_server.h -wrappers: wrapfuncs.in $(USE_64) makewrappers +wrappers: wrapfuncs.in $(USE_64) makewrappers $(TEMPLATES) ./makewrappers wrapfuncs.in $(USE_64) .SECONDARY: wrappers diff --git a/makewrappers b/makewrappers index c2e6cf6..1166fe8 100755 --- a/makewrappers +++ b/makewrappers @@ -1,463 +1,451 @@ -#!/bin/sh -# -# makewrappers, script to auto-generate wrapper functions -# -# Copyright (c) 2008-2010 Wind River Systems, Inc. -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the Lesser GNU General Public License version 2.1 as -# published by the Free Software Foundation. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -# See the Lesser GNU General Public License for more details. -# -# You should have received a copy of the Lesser GNU General Public License -# version 2.1 along with this program; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -# - -case $# in -0) echo >&2 "Usage: makewrappers file [...]" - exit 1 - ;; -esac - -# save old versions -test -f "pseudo_wrapfuncs.c" && mv pseudo_wrapfuncs.c pseudo_wrapfuncs.c.old -test -f "pseudo_wrapfuncs.h" && mv pseudo_wrapfuncs.h pseudo_wrapfuncs.h.old - -# create files -exec 5>pseudo_wrapfuncs.c -exec 6>pseudo_wrapfuncs.h -exec 7>pseudo_wrapper_table.c - -# "cat >&N <<EOF" populates the file on &N with the here-document. - -# pseudo_wrapfuncs.c has to have all the hunks used by the wrapper functions, -# including guts/*.c. -cat >&5 <<EOF -`cat guts/COPYRIGHT` -/* wrapper functions. generated automatically. */ - -/* This file is generated and should not be modified. See the makewrappers - * script if you want to modify this. */ -EOF - -cat >&6 <<EOF -`cat guts/COPYRIGHT` -EOF - -# the wrapper function table is also #included, it is a separate file so it -# can be written as we go, but still be a single table. -cat >&7 <<EOF -`cat guts/COPYRIGHT` -/* The table of wrapper functions to populate */ - -/* This file is generated and should not be modified. See the makewrappers - * script if you want to modify this. */ -static struct { - char *name; /* the name */ - int (**real)(void); /* the underlying syscall */ - int (*dummy)(void); /* the always-fails form */ - int (*wrapper)(void); /* the wrapper from guts/name.c */ -} pseudo_functions[] = { -EOF - -printf >&2 'Reading signatures...\n' -for file in "$@" -do - # read lines containing wrappable functions, write wrapper - # declarations, definitions, and so on. - printf >&2 '[%s]' "$file" - while read signature - do - # skip comments - case $signature in - \#*) continue;; - esac - # obtain return type, name, and arguments - args=`expr "$signature" : '[^(]*(\(.*\));'` - modifiers=`expr "$signature" : '.*; /\* \(.*\) \*/'` - return_and_name=`expr "$signature" : '\([^(]*\)('` - name=`expr "$return_and_name" : '.*[^a-zA-Z0-9_]\([a-zA-Z0-9_]*\)$'` - type=`expr "$return_and_name" : '\(.*[^ ]\) *'"$name"'$'` - printf >&2 ' %s' "$name" - wrapargnames='' - argnames='' - # for handling path canonicalization - pathnames='' - flags='0' - dirfd='AT_FDCWD' - - save_IFS=$IFS - IFS=, - set -- $args - IFS=$save_IFS - args='' - dummy_args='' - wrap_args='' - va_list_abort_on_real='' - optional_arg=false - make_va_list=false - maybe_va_end='' - prepend='' - depth=0 - for arg - do - - # handle optional arguments, like the third arg - # to open() - if [ $depth -gt 0 ]; then - case $arg in - *\)*) - lcount=`echo $arg | tr -cd '(' | wc -c` - rcount=`echo $arg | tr -cd ')' | wc -c` - depth=`expr $depth + $lcount - $rcount` - prepend="${prepend:+$prepend,}$arg" - arg="" - ;; - *) prepend="${prepend:+$prepend,}$arg" - arg="" - ;; - esac - else - case $arg in - *\(*) lcount=`echo $arg | tr -cd '(' | wc -c` - rcount=`echo $arg | tr -cd ')' | wc -c` - depth=`expr $depth + $lcount - $rcount` - prepend="${prepend:+$prepend,}$arg" - arg="" - ;; - esac - fi - # we're inside nested ()s - if [ $depth -gt 0 ]; then - continue - fi - arg="$prepend$arg" - # strip whitespace - arg=${arg# } - arg=${arg% } - prepend='' - case $arg in - ...) - make_va_list=true - maybe_va_end='va_end(ap);' - va_list_abort_on_real='assert(!"cannot chain to real versions of variadic functions");' - args="$args${args+, }..." - dummy_args="$dummy_args${dummy_args:+, }..." - wrap_args="$wrap_args${wrap_args:+, }va_list ap" - arg="..." - argname="ap" - argnames="$argnames${argnames:+, }ap" - wrapargnames="$wrapargnames${wrapargnames:+, }ap" - # used for creating a va_list - optional_prev=$prev_argname - ;; - ...*) - optional_arg=true - args="$args${args:+, }..." - wrap_args="$wrap_args${wrap_args:+, }..." - dummy_args="$dummy_args${dummy_args:+, }..." - arg=`expr "$arg" : '\.\.\.{\(.*\)}'` - argname=`expr "$arg" : '.*[^a-zA-Z0-9_]\([a-zA-Z0-9_]*\)$'` - argnames="$argnames${argnames:+, }$argname" - wrapargnames="$wrapargnames${wrapargnames:+, }$argname" - - # we need this to extract and pass the argument - optional_decl=$arg - optional_prev=$prev_argname - optional_name=$argname - optional_type=`expr "$arg" : '\(.*[^ ]\) *'"$argname"'$'` - ;; - *\(*) # function pointer - argname=`expr "$arg" : '[^(]*(\*\([a-zA-Z0-9_]*\).*'` - args="$args${args:+, }$arg" - wrap_args="$wrap_args${wrap_args:+, }$arg" - dummy_args="$dummy_args${dummy_args:+, }$arg __attribute__((unused))" - wrapargnames="$wrapargnames${wrapargnames:+, }$argname" - argnames="$argnames${argnames:+, }$argname" - prev_argname=$argname - ;; - *) - argname=`expr "$arg" : '.*[^a-zA-Z0-9_](*\([a-zA-Z0-9_]*\))*(*)*$'` - args="$args${args:+, }$arg" - wrap_args="$wrap_args${wrap_args:+, }$arg" - dummy_args="$dummy_args${dummy_args:+, }$arg __attribute__((unused))" - # special handling for canonicalization - # set this before changing path -> rpath, for guts files - wrapargnames="$wrapargnames${wrapargnames:+, }$argname" - case $argname in - *path) pathnames="${pathnames+${pathnames} }$argname" - argname="r$argname" - ;; - dirfd) dirfd='dirfd';; - flags) flags='flags';; - esac - argnames="$argnames${argnames:+, }$argname" - prev_argname=$argname - ;; - esac - done - # see whether flags was overridden - flags_tmp=`expr "$modifiers" : 'flags=\([^ ]*\)'` - if [ -n "$flags_tmp" ]; then - flags=$flags_tmp - fi - decl_paths='' - alloc_paths='' - free_paths='' - # any argument ending in "path" is presumed to need to be - # converted to a chroot path. To avoid this, name the - # argument something else (e.g. "template" for mkstemp) - for p in $pathnames; do - decl_paths="${decl_paths} - char *r$p = (char *) $p;" - alloc_paths="${alloc_paths} - r$p = pseudo_root_path(__func__, __LINE__, $dirfd, $p, $flags);" - free_paths="${free_paths} - free(r$p);" - done - - # determine default return value. - is_void=false - case $type in - int|ssize_t|long) - default_value=-1;; - uid_t|gid_t) - default_value=0;; - *'*') - default_value=NULL;; - void) - is_void=true - default_value='' - ;; - *) echo >&2 " -Unknown type '$type'." ; exit 1 ;; - esac - if $is_void; then - write_return() { - printf "return" - } - write_assign() { - printf "(void)" - } - write_decl() { - : # do nothing - } - else - write_return() { - printf "return %s" "$1" - } - write_assign() { - printf "%s =" "$1" - } - write_decl() { - printf "%s %s = %s;" "$1" "$2" "$3" - } - fi - # create the wrappers - # first the dummy, and the function pointer: - cat >&5 <<EOF -static $type -dummy_$name($dummy_args) { - pseudo_enosys("$name"); - errno = ENOSYS; - $(write_return $default_value); -} - -static $type (*real_$name)($args) = dummy_$name; - -EOF - # then the wrapper signature and args: - if $optional_arg; then - cat >&5 <<EOF -$type -$name($args) { - $optional_decl; - va_list ap; - va_start(ap, $optional_prev); - $optional_name = va_arg(ap, $optional_type); - va_end(ap); - -EOF - else - cat >&5 <<EOF -$type -$name($args) { -EOF - fi - if $make_va_list; then - cat >&5 <<EOF - va_list ap; - va_start(ap, $optional_prev); -EOF - fi - # and now the body of the wrapper: - cat >&5 <<EOF - sigset_t blocked, saved; - $(write_decl "$type" "rc" "$default_value") - - pseudo_debug(4, "called: $name\n"); - /* these are signals for which the handlers often - * invoke operations, such as close(), which are handled - * by pseudo and could result in a deadlock. - */ - sigemptyset(&blocked); - sigaddset(&blocked, SIGALRM); /* every-N-seconds tasks */ - sigaddset(&blocked, SIGCHLD); /* reaping child processes */ - sigaddset(&blocked, SIGHUP); /* idiomatically, reloading config */ - sigaddset(&blocked, SIGTERM); /* shutdown/teardown operations */ - sigaddset(&blocked, SIGUSR1); /* reopening log files, sometimes */ - sigaddset(&blocked, SIGUSR2); /* who knows what people do */ - sigprocmask(SIG_BLOCK, &blocked, &saved); - if (pseudo_getlock()) { - errno = EBUSY; - sigprocmask(SIG_SETMASK, &saved, NULL); - $(write_return $default_value); - } - $decl_paths - if (pseudo_populate_wrappers()) { - int save_errno; - if (antimagic > 0) { - if (real_$name) { - /* if this function takes ..., there is - * no way to pass the real argument list - * to it... - */ - $va_list_abort_on_real - $(write_assign rc) (*real_$name)($argnames); - } else { - $(write_assign rc) dummy_$name($argnames); - } - } else { -$alloc_paths - /* exec*() use this to restore the sig mask */ - pseudo_saved_sigmask = saved; - $(write_assign rc) wrap_$name($argnames); -$free_paths - } - $maybe_va_end - save_errno = errno; - pseudo_droplock(); - sigprocmask(SIG_SETMASK, &saved, NULL); - pseudo_debug(4, "completed: $name\n"); - errno = save_errno; - $(write_return rc); - } else { - pseudo_droplock(); - sigprocmask(SIG_SETMASK, &saved, NULL); - pseudo_debug(4, "completed: $name\n"); - $(write_assign rc) dummy_$name($argnames); - $maybe_va_end - $(write_return rc); - } -} - -EOF - # and now the signature part for the actual implementation: - # and the guts include file. - - # the wrapper function is actually declared in - # pseudo_wrapper.c, with guts implemented in a separate - # file with comments indicating the signature. - guts="guts/$name.c" - - # the actual wrapper function, including argument setup - if $optional_arg; then - cat >&5 << EOF -static $type -wrap_$name($wrap_args) { - $(write_decl "$type" "rc" "$default_value") - $optional_decl; - - va_list ap; - va_start(ap, $optional_prev); - $optional_name = va_arg(ap, $optional_type); - va_end(ap); - -#include "$guts" - - $(write_return rc); -} -EOF - else - cat >&5 << EOF -static $type -wrap_$name($wrap_args) { - $(write_decl "$type" "rc" "$default_value") - -#include "$guts" - - $(write_return rc); -} -EOF - fi - - # if the guts file didn't already exist, create a default. - if test ! -f "$guts"; then - if $optional_arg; then - cat > "$guts" <<EOF -/* - * Copyright (c) `date +%Y` Wind River Systems; see - * guts/COPYRIGHT for information. - * - * static $type - * wrap_$name($args$optional_decl) { - * $(write_decl "$type" "rc" "$default_value") - */ - - $(write_assign rc) real_$name($wrapargnames); - -/* $(write_return rc); - * } - */ -EOF - else - cat > "$guts" <<EOF -/* - * Copyright (c) `date +%Y` Wind River Systems; see - * guts/COPYRIGHT for information. - * - * static $type - * wrap_$name($wrap_args) { - * $(write_decl "$type" "rc" "$default_value") - */ - - $(write_assign rc) real_$name($wrapargnames); - -/* $(write_return rc); - * } - */ -EOF - fi - fi - # prototypes for pseudo_wrapfuncs.h - cat >&6 <<EOF -/* $type $name($args); */ -static $type dummy_$name($args); -static $type wrap_$name($wrap_args); -static $type (*real_$name)($args); - -EOF - - # and entries in the Big Table - cat >&7 <<EOF - { /* $type $name($args); */ - "$name", - (int (**)(void)) &real_$name, - (int (*)(void)) dummy_$name, - (int (*)(void)) wrap_$name - }, -EOF - done < $file -done -printf >&2 '.\n' - -# sentinel values -cat >&7 <<EOF - { NULL, NULL, NULL, NULL }, -}; -EOF +#!/usr/bin/env python +'convert wrapfuncs.in to wrapper function stubs and tables' + +import glob +import sys +import re +import os +import string +import datetime +import time +from string import Template + +class SourceFile(object): + "A template for creating a source file" + + def __init__(self, path): + # default values... + # no name or file yet + self.name = '' + self.sections = {} + self.file = None + self.path = None + # open a new file for each function + self.file_per_func = False + + # empty footer if none specified: + self.sections['footer'] = [] + + # lines appended to body by default + self.sections['body'] = [] + current = self.sections['body'] + + self.f = file(path, 'r') + if not self.f: + return None + for line in self.f: + line = line.rstrip() + if line.startswith('@'): + if ' ' in line: + leading, trailing = line.split(' ', 1) + else: + leading, trailing = line, None + + if leading == '@name': + if not trailing: + raise Exception("@name requires a file name.") + self.path = trailing + if '$' in self.path: + self.file_per_func = True + else: + section = leading[1:] + if not section in self.sections: + self.sections[section] = [] + current = self.sections[section] + else: + current.append(line) + for section, data in self.sections.items(): + self.sections[section] = Template("\n".join(data)) + + # You need a file if this isn't a file-per-func + if not self.file_per_func: + self.file = file(self.path, 'w') + if not self.file: + raise IOError("Couldn't open %s to read a template." % self.path) + + def __del__(self): + if self.file: + self.file.close() + + def __repr__(self): + strings = [] + if self.file_per_func: + strings.append("path: %s (per func)" % self.path) + else: + strings.append("path: %s" % self.path) + for name, data in self.sections.items(): + strings.append("%s:" % name) + strings.append(data.safe_substitute({})) + return "\n".join(strings) + + def emit(self, template, func = None): + if self.file_per_func: + if not func: + # print "Must have a function to emit any part of a file-per-func template." + return + path = Template(self.path).safe_substitute(func) + if os.path.exists(path): + # print "We don't overwrite existing files." + return + self.file = file(path, 'w') + if not self.file: + print "Couldn't open '%s' (expanded from %s), not emitting '%s'." % (path, self.path, template) + return + + if template == "copyright": + # hey, at least it's not a global variable, amirite? + self.file.write(SourceFile.copyright) + elif template in self.sections: + templ = self.sections[template] + self.file.write(templ.safe_substitute(func)) + self.file.write("\n") + else: + print "Warning: Unknown template '%s'." % template + + if self.file_per_func: + self.file.close() + self.file = None + +class ArgumentList: + "A (possibly empty) list of arguments" + + def __init__(self, text): + "parse a comma-separated argument list (may contain function prototypes)" + self.args = [] + self.variadic = False + self.variadic_decl = "" + self.variadic_start = "" + self.variadic_end = "" + self.prologue_call_real = "/* pass the call on to the underlying syscall */" + # (void) is an empty list, not a list of a single argument which is void + if text == "void": + return + + depth = 0 + accum = '' + comma_sep = text.split(', ') + # now, what if there was a comma embedded in an argument? + for arg in comma_sep: + lcount = arg.count('(') + rcount = arg.count(')') + depth = depth + lcount - rcount + if (depth > 0): + accum += arg + ', ' + else: + self.args.append(C_Argument(accum + arg)) + accum = '' + if depth != 0: + raise Exception("mismatched ()s while parsing '%s'" % text) + if self.args[-1].vararg: + self.variadic = True + self.variadic_arg = self.args[-1] + self.last_fixed_arg = self.args[-2].name + self.variadic_decl = "va_list ap;\n" + self.variadic_start = "va_start(ap, %s);\n" % self.last_fixed_arg + if self.variadic_arg.vararg_wraps: + self.variadic_decl += "\t%s;\n" % self.variadic_arg.vararg_wraps.decl() + self.variadic_start += "\t%s = va_arg(ap, %s);\n\tva_end(ap);\n" % (self.variadic_arg.name, self.variadic_arg.type) + else: + self.variadic_end = "va_end(ap);\n" + self.prologue_call_real = '/* no way to pass a ... */\n\t\t\t\tassert(!"cannot chain to real versions of variadic functions");' + + def decl(self, **kw): + if not self.args: + return "void" + + list = map(lambda x: x.decl(**kw), self.args) + return ', '.join(list) + + def call(self): + if not self.args: + return "" + list = map(lambda x: x.call(), self.args) + return ', '.join(list) + + def __repr__(self): + if not self.args: + return "no arguments" + else: + return '::'.join(map(lambda x:x.decl(), self.args)) + + +class C_Argument: + "A function argument such as 'char *path' or 'char (*foo)(void)'" + def __init__(self, text): + "get the type and name of a trivial C declaration" + self.vararg = False + self.function_pointer = False + self.spacer = '' + + if text == 'void': + raise Exception("Tried to declare a nameless object of type void.") + + if text.startswith('...'): + self.vararg = True + if len(text) > 3: + # we're a wrapper for something else, declared as + # ...{real_decl}, as in the third argument to open(2) + text = text[4:-1] + # stash a copy of these values without the vararg flag, so we can + # declare them prettily later + self.vararg_wraps = C_Argument(text) + else: + # nothing to do. + self.vararg_wraps = None + self.type, self.name = None, None + return + else: + self.vararg = False + + # try for a function pointer + match = re.match('(.*)\(\*([a-zA-Z0-9_]*)\)\((.*)\)', text) + if match: + self.function_pointer = True + self.args = match.group(3) + ret_type = match.group(1) + args = match.group(3) + self.fulltype = "$ret_type(*)($args)" + self.name = match.group(2).rstrip(' ') + self.type = ret_type + else: + # plain declaration + match = re.match('(.*[ *])\(?\*?([a-zA-Z0-9_]*)\)?', text) + # there may not be a match, say in the special case where an arg is '...' + if match: + self.type, self.name = match.group(1).rstrip(' '), match.group(2) + else: + self.type, self.name = None, None + + # spacing between type and name, needed if type ends with a character + # which could be part of an identifier + if re.match('[_a-zA-Z0-9]', self.type[-1]): + self.spacer = ' ' + + def decl(self, **kw): + comment = kw.get('comment', False) + if self.function_pointer: + decl = "%s%s(*%s)(%s)" % (self.type, self.spacer, self.name, self.args) + else: + decl = "%s%s%s" % (self.type, self.spacer, self.name) + + if self.vararg: + if self.vararg_wraps: + if comment: + decl = "... { %s }" % decl + else: + decl = "... /* %s */" % decl + else: + decl = "..." + return decl + + def call(self): + if self.type == 'void': + return '' + + if self.vararg and not self.vararg_wraps: + return "ap" + + return self.name + + def str(self): + return self.decl() + + def __repr__(self): + return self.decl() + +class C_Function: + "A function signature and additional data about how the function works" + def __init__(self, line): + # table of known default values: + default_values = { 'gid_t': '0', 'uid_t': '0', 'int': '-1', 'long': '-1', 'ssize_t': '-1' } + + self.dirfd = 'AT_FDCWD' + self.flags = '0' + self.paths_to_munge = [] + # used for the copyright date when creating stub functions + self.date = datetime.date.today().year + + function, comments = line.split(';') + comment = re.search('/\* *(.*) *\*/', comments) + if comment: + self.comments = comment.group(1) + else: + self.comments = None + + bits = re.match('([^(]*)\((.*)\)', function) + x = C_Argument(bits.group(1)) + self.type, self.name = x.type, x.name + # it's convenient to have this declared here so we can use its .decl later + if self.type != 'void': + self.rc = C_Argument("%s rc" % x.type) + + # Some args get special treatment: + # * If the arg has a name ending in 'path', we will canonicalize it. + # * If the arg is named 'dirfd' or 'flags', it becomes the default + # values for the dirfd and flags arguments when canonicalizing. + # * Note that the "comments" field (/* ... */ after the decl) can + # override the dirfd/flags values. + self.args = ArgumentList(bits.group(2)) + for arg in self.args.args: + # ignore varargs, they never get these special treatments + if arg.vararg: + pass + elif arg.name == 'dirfd': + self.dirfd = 'dirfd' + elif arg.name == 'flags': + self.flags = 'flags' + elif arg.name[-4:] == 'path': + self.paths_to_munge.append(arg.name) + + # pick default values + if self.type == 'void': + self.default_value = '' + elif self.type[-1:] == '*': + self.default_value = 'NULL' + else: + try: + self.default_value = default_values[self.type] + except KeyError: + raise Exception("Function %s has return type %s, for which there is no default value." % (self.name, self.type)) + + # handle special comments, such as flags=AT_SYMLINK_NOFOLLOW + if self.comments: + modifiers = self.comments.split(', ') + for mod in modifiers: + key, value = mod.split('=') + value = value.rstrip(' ') + setattr(self, key, value) + + def comment(self): + return self.decl(comment = True) + + def decl(self, **kw): + if self.type[-1:] == '*': + spacer = '' + else: + spacer = ' ' + return "%s%s%s(%s)" % (self.type, spacer, self.name, self.args.decl(**kw)) + + def decl_args(self): + return self.args.decl() + + def wrap_args(self): + return self.args.decl(wrap = True) + + def call_args(self): + return self.args.call() + + def alloc_paths(self): + alloc_paths = [] + for p in self.paths_to_munge: + alloc_paths.append("%s = pseudo_root_path(__func__, __LINE__, %s, %s, %s);" % (p, self.dirfd, p, self.flags)) + return "\n\t\t\t".join(alloc_paths); + + def free_paths(self): + free_paths = [] + # the cast is here because free's argument isn't const qualified, but + # the original path may have been -- but we only GET here if the path has + # been overwritten. + for p in self.paths_to_munge: + free_paths.append("free((void *) %s);" % p) + return "\n\t\t\t".join(free_paths); + + def rc_return(self): + if self.type == 'void': + return "return;" + else: + return "return rc;" + + def rc_decl(self): + if self.type == 'void': + return "" + else: + return "%s = %s;" % (self.rc.decl(), self.default_value) + + def rc_assign(self): + if self.type == 'void': + return "(void)" + else: + return "rc =" + + def def_return(self): + if self.type == 'void': + return "return;" + else: + return "return %s;" % self.default_value + + def __getitem__(self, key): + "Make this object look like a dict for Templates to use" + try: + attr = getattr(self, key) + except AttributeError: + # There's a few attributes that are handled inside the args object, so check there + # too... + attr = getattr(self.args, key) + + if callable(attr): + return attr() + else: + return attr + + def __repr__(self): + pretty = "%(name)s returns %(type)s and takes " % self + pretty += repr(self.args) + if self.comments: + pretty += ' (%s)' % self.comments + return pretty + +files = {} + +def main(): + funcs = [] + sources = [] + + # error checking helpfully provided by the exception handler + copyright_file = file('guts/COPYRIGHT', 'r') + SourceFile.copyright = copyright_file.read() + copyright_file.close() + + for path in glob.glob('templates/*'): + try: + source = SourceFile(path) + source.emit('copyright') + source.emit('header') + sources.append(source) + except IOError: + print "Invalid or malformed template %s. Aborting." % path + exit(1) + + for filename in sys.argv[1:]: + "parse the list of functions" + sys.stdout.write("%s: " % filename) + f = file(filename, 'r') + for line in f: + line = line.rstrip(" \r\n") + if line.startswith('#') or line == "": + continue + try: + func = C_Function(line) + funcs.append(func) + sys.stdout.write(".") + except Exception as e: + print "Parsing failed:", e + exit(1) + f.close() + print "" + + # the per-function stuff + print "Writing functions...", + for func in funcs: + "populate various tables and files with each function" + for source in sources: + source.emit('body', func) + print "done. Cleaning up." + + for source in sources: + "clean up files" + source.emit('footer') + del source + +if __name__ == '__main__': + main() diff --git a/pseudo_wrappers.c b/pseudo_wrappers.c index 3ea0952..dbcfac8 100644 --- a/pseudo_wrappers.c +++ b/pseudo_wrappers.c @@ -40,6 +40,7 @@ static int pseudo_mutex_recursion = 0; static int pseudo_getlock(void); static void pseudo_droplock(void); static size_t pseudo_dechroot(char *, size_t); +static void pseudo_sigblock(sigset_t *); extern char *program_invocation_short_name; static sigset_t pseudo_saved_sigmask; @@ -48,6 +49,24 @@ static sigset_t pseudo_saved_sigmask; #include "pseudo_wrapper_table.c" #include "pseudo_wrapfuncs.c" +static void +pseudo_sigblock(sigset_t *saved) { + sigset_t blocked; + + /* these are signals for which the handlers often + * invoke operations, such as close(), which are handled + * by pseudo and could result in a deadlock. + */ + sigemptyset(&blocked); + sigaddset(&blocked, SIGALRM); /* every-N-seconds tasks */ + sigaddset(&blocked, SIGCHLD); /* reaping child processes */ + sigaddset(&blocked, SIGHUP); /* idiomatically, reloading config */ + sigaddset(&blocked, SIGTERM); /* shutdown/teardown operations */ + sigaddset(&blocked, SIGUSR1); /* reopening log files, sometimes */ + sigaddset(&blocked, SIGUSR2); /* who knows what people do */ + sigprocmask(SIG_BLOCK, &blocked, saved); +} + static int pseudo_getlock(void) { if (pthread_equal(pseudo_mutex_holder, pthread_self())) { @@ -90,6 +109,7 @@ pseudo_enosys(const char *func) { if (value) abort(); free(value); + errno = ENOSYS; } /* de-chroot a string. @@ -132,13 +152,13 @@ pseudo_populate_wrappers(void) { pseudo_getlock(); pseudo_antimagic(); for (i = 0; pseudo_functions[i].name; ++i) { - if (*pseudo_functions[i].real == pseudo_functions[i].dummy) { + if (*pseudo_functions[i].real == NULL) { int (*f)(void); char *e; dlerror(); f = dlsym(RTLD_NEXT, pseudo_functions[i].name); if ((e = dlerror()) != NULL) { - /* leave it pointed to dummy */ + /* leave it NULL, which our implementation checks for */ pseudo_diag("No wrapper for %s: %s\n", pseudo_functions[i].name, e); } else { if (f) diff --git a/templates/guts b/templates/guts new file mode 100644 index 0000000..3e32fb2 --- /dev/null +++ b/templates/guts @@ -0,0 +1,15 @@ +@name guts/${name}.c +@body +/* + * Copyright (c) ${date} Wind River Systems; see + * guts/COPYRIGHT for information. + * + * ${comment} + * ${rc_decl} + */ + + ${rc_assign} real_${name}(${call_args}); + +/* ${rc_return} + * } + */ diff --git a/templates/wrapfuncs.c b/templates/wrapfuncs.c new file mode 100644 index 0000000..480930d --- /dev/null +++ b/templates/wrapfuncs.c @@ -0,0 +1,78 @@ +@name pseudo_wrapfuncs.c +@header +/* wrapper functions. generated automatically. */ + +/* IF YOU ARE SEEING COMPILER ERRORS IN THIS FILE: + * If you are seeing a whole lot of errors, make sure you aren't actually + * trying to compile pseudo_wrapfuncs.c directly. This file is #included + * from pseudo_wrappers.c, which has a lot of needed include files and + * static declarations. + */ + +/* This file is generated and should not be modified. See the makewrappers + * script if you want to modify this. */ +@body + +static ${type} (*real_${name})(${decl_args}) = NULL; + +${type} +${name}(${decl_args}) { + sigset_t saved; + ${variadic_decl} + ${rc_decl} + + ${variadic_start} + + pseudo_debug(4, "called: ${name}\n"); + pseudo_sigblock(&saved); + if (pseudo_getlock()) { + errno = EBUSY; + sigprocmask(SIG_SETMASK, &saved, NULL); + ${def_return} + } + if (pseudo_populate_wrappers()) { + int save_errno; + if (antimagic > 0) { + if (real_$name) { + ${prologue_call_real} + ${rc_assign} (*real_${name})(${call_args}); + } else { + /* rc was initialized to the "failure" value */ + pseudo_enosys("${name}"); + } + } else { + ${alloc_paths} + /* exec*() use this to restore the sig mask */ + pseudo_saved_sigmask = saved; + ${rc_assign} wrap_$name(${call_args}); + ${free_paths} + } + ${variadic_end} + save_errno = errno; + pseudo_droplock(); + sigprocmask(SIG_SETMASK, &saved, NULL); + pseudo_debug(4, "completed: $name\n"); + errno = save_errno; + ${rc_return} + } else { + pseudo_droplock(); + sigprocmask(SIG_SETMASK, &saved, NULL); + pseudo_debug(4, "completed: $name\n"); + /* rc was initialized to the "failure" value */ + pseudo_enosys("${name}"); + ${variadic_end} + ${rc_return} + } +} + +static ${type} +wrap_${name}(${wrap_args}) { + $rc_decl + ${variadic_decl} + ${variadic_start} + +#include "guts/${name}.c" + + ${rc_return} +} + diff --git a/templates/wrapfuncs.h b/templates/wrapfuncs.h new file mode 100644 index 0000000..63ba903 --- /dev/null +++ b/templates/wrapfuncs.h @@ -0,0 +1,10 @@ +@name pseudo_wrapfuncs.h +@header +/* wrapper functions. generated automatically. */ + +/* This file is generated and should not be modified. See the makewrappers + * script if you want to modify this. */ +@body +/* ${comment} */ +static ${type} wrap_${name}(${wrap_args}); +static ${type} (*real_${name})(${decl_args}); diff --git a/templates/wrapper_table b/templates/wrapper_table new file mode 100644 index 0000000..a16dca9 --- /dev/null +++ b/templates/wrapper_table @@ -0,0 +1,20 @@ +@name pseudo_wrapper_table.c +@header +/* The table of wrapper functions to populate */ + +/* This file is generated and should not be modified. See the makewrappers + * script if you want to modify this. */ +static struct { + char *name; /* the name */ + int (**real)(void); /* the underlying syscall */ + int (*wrapper)(void); /* the wrapper from guts/name.c */ +} pseudo_functions[] = { +@body + { /* ${comment}; */ + "${name}", + (int (**)(void)) &real_${name}, + (int (*)(void)) wrap_${name} + }, +@footer + { NULL, NULL, NULL }, +}; |