aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--ChangeLog.txt15
-rw-r--r--guts/README16
-rw-r--r--guts/fork.c17
-rwxr-xr-xmaketables68
-rwxr-xr-xmakewrappers10
-rw-r--r--pseudo.110
-rw-r--r--pseudo.h10
-rw-r--r--pseudo_client.c42
-rw-r--r--pseudo_db.c2
-rw-r--r--pseudo_util.c58
-rw-r--r--pseudo_wrappers.c350
-rw-r--r--templates/wrapfuncs.c2
-rw-r--r--templates/wrapfuncs.h8
-rw-r--r--templates/wrapper_table11
-rwxr-xr-xtest/test-forkoff-env_i.sh30
-rwxr-xr-xtest/test-forkoff.sh18
-rw-r--r--wrapfuncs.in5
17 files changed, 595 insertions, 77 deletions
diff --git a/ChangeLog.txt b/ChangeLog.txt
index 8e737e4..121fe12 100644
--- a/ChangeLog.txt
+++ b/ChangeLog.txt
@@ -1,3 +1,18 @@
+2010-12-07:
+ * (seebs) whitespace fixes
+ * (seebs) improve fork and PSEUDO_DISABLED
+ * (seebs) add support for clone(2)
+ * (mhatle) rework/improve clone(2) support
+ * (mhatle) add test code for PSEUDO_DISABLED
+
+2010-12-02:
+ * (seebs) rework of fork/exec, add PSEUDO_DISABLED
+
+2010-11-30:
+ * (seebs) move *_t types to a separate file.
+ * (seebs) remove unused tables from pseudo_db.c
+ * (seebs) cleanup .gitignore
+
2010-11-17:
* (seebs) add "Futures.txt" notes about future development plans
* (seebs) split some of the templating code out of makewrappers
diff --git a/guts/README b/guts/README
index 8bfbfe6..0a1fe5f 100644
--- a/guts/README
+++ b/guts/README
@@ -83,7 +83,7 @@ wrappers:
close
dup
dup2
- excl* (all redirect through execve)
+ execl* (no guts implementations; see pseudo_wrappers.c)
execv
execve
execvp
@@ -97,7 +97,6 @@ wrappers:
fcntl
fork
link
- vfork
The following functions don't have any direct database interactions,
but are used to simulate the permissions system:
@@ -206,3 +205,16 @@ needed because we don't actually track or manage extended attributes, but
a few programs attempt to use *setxattr() to set regular permissions,
and only use a regular chmod if the *setxattr() call returns -1 and
sets errno to ENOTSUP.
+
+ fgetxattr
+ flistxattr
+ fremovexattr
+ fsetxattr
+ getxattr
+ lgetxattr
+ listxattr
+ llistxattr
+ lremovexattr
+ lsetxattr
+ removexattr
+ setxattr
diff --git a/guts/fork.c b/guts/fork.c
index 4a91f7b..fa23be1 100644
--- a/guts/fork.c
+++ b/guts/fork.c
@@ -6,10 +6,19 @@
* wrap_fork(void) {
* int rc = -1;
*/
- rc = real_fork();
- if (rc == 0)
- pseudo_client_reset();
-
+ if (real_fork) {
+ rc = real_fork();
+ /* special case: we may want to enable or disable
+ * pseudo in the child process
+ */
+ if (rc == 0) {
+ pseudo_setupenv();
+ pseudo_client_reset();
+ }
+ } else {
+ /* rc was initialized to the "failure" value */
+ pseudo_enosys("fork");
+ }
/* return rc;
* }
*/
diff --git a/maketables b/maketables
index 4e7d6ea..51fb4c8 100755
--- a/maketables
+++ b/maketables
@@ -50,8 +50,6 @@ value. (This is for consistency with C array bounds.)
import glob
import sys
-import re
-import datetime
import string
from templatefile import TemplateFile
@@ -63,11 +61,11 @@ class DataType:
source = file(path)
definition = source.readline().rstrip()
self.name, qualifiers = string.split(definition, ': ', 2)
- if '; ' in qualifiers:
+ if '; ' in qualifiers:
self.prefix, columns = string.split(qualifiers, '; ')
- else:
- self.prefix = qualifiers
- columns = []
+ else:
+ self.prefix = qualifiers
+ columns = []
if len(columns):
self.columns = []
columns = string.split(columns, ', ')
@@ -80,23 +78,23 @@ class DataType:
else:
self.columns = []
self.data = []
- self.comments = []
+ self.comments = []
for line in source.readlines():
item = {}
- if line.startswith('#'):
- self.comments.append(line.rstrip().replace('#', ''))
- continue
+ if line.startswith('#'):
+ self.comments.append(line.rstrip().replace('#', ''))
+ continue
# first entry on the line is the "real" name/id, following hunks
# are additional columns
cols = string.split(line.rstrip(), ', ')
item["name"] = cols.pop(0)
- item["upper"] = item["name"].replace('-', '_').upper()
+ item["upper"] = item["name"].replace('-', '_').upper()
column_list = []
for col in self.columns:
if len(cols) > 0:
- column_list.append({"name":col["name"], "value":cols.pop(0)})
+ column_list.append({"name":col["name"], "value":cols.pop(0)})
else:
- column_list.append({"name":col["name"], "value":col["default"]})
+ column_list.append({"name":col["name"], "value":col["default"]})
item["cols"] = column_list
self.data.append(item)
@@ -110,22 +108,22 @@ class DataType:
return attr
def __repr__(self):
- str = ""
- str += "type: %s_t" % self.name
- str += " %s_ENUM\n" % self.prefix
+ out = ""
+ out += "type: %s_t" % self.name
+ out += " %s_ENUM\n" % self.prefix
for col in self.columns:
- str += "\tcol: %s (%s)\n" % (col["name"], col["value"])
+ out += "\tcol: %s (%s)\n" % (col["name"], col["value"])
for item in self.data:
- str += "item: %s\n" % item["name"]
+ out += "item: %s\n" % item["name"]
for col in item["cols"]:
- str += "\t%s(%s)\n" % (col["name"], col["value"])
- return str
+ out += "\t%s(%s)\n" % (col["name"], col["value"])
+ return out
def comment(self):
- if len(self.comments):
- return '/*' + '\n *'.join(self.comments) + ' */\n'
- else:
- return ''
+ if len(self.comments):
+ return '/*' + '\n *'.join(self.comments) + ' */\n'
+ else:
+ return ''
def names(self):
return ',\n\t'.join('"%s"' % x["name"] for x in self.data)
@@ -141,7 +139,7 @@ class DataType:
decl_lines.append('\t"%s",' % col["value"])
for item in self.data:
decl_lines.append('\t"%s",' % item["cols"][column]["value"])
- decl_lines.append('\tNULL')
+ decl_lines.append('\tNULL')
decl_lines.append("};")
column = column + 1
return '\n'.join(decl_lines)
@@ -149,21 +147,21 @@ class DataType:
def column_funcs(self):
decl_lines = []
for col in self.columns:
- decl_lines.append('extern const char *')
- decl_lines.append('pseudo_%s_%s(pseudo_%s_t id) {' %
- (self.name, col["name"], self.name))
- decl_lines.append('\tif (id < 0 || id >= %s_MAX)' % (self.prefix))
- decl_lines.append('\t\treturn "%s";' % col["value"])
- decl_lines.append('\treturn %s_id_to_%s[id];' %
- (self.name, col["name"]))
- decl_lines.append('}')
+ decl_lines.append('extern const char *')
+ decl_lines.append('pseudo_%s_%s(pseudo_%s_t id) {' %
+ (self.name, col["name"], self.name))
+ decl_lines.append('\tif (id < 0 || id >= %s_MAX)' % (self.prefix))
+ decl_lines.append('\t\treturn "%s";' % col["value"])
+ decl_lines.append('\treturn %s_id_to_%s[id];' %
+ (self.name, col["name"]))
+ decl_lines.append('}')
return '\n'.join(decl_lines)
def column_protos(self):
decl_lines = []
for col in self.columns:
- decl_lines.append('extern const char *pseudo_%s_%s(pseudo_%s_t id);' %
- (self.name, col["name"], self.name))
+ decl_lines.append('extern const char *pseudo_%s_%s(pseudo_%s_t id);' %
+ (self.name, col["name"], self.name))
return '\n'.join(decl_lines)
def main():
diff --git a/makewrappers b/makewrappers
index 5ec58f8..fba2fce 100755
--- a/makewrappers
+++ b/makewrappers
@@ -33,8 +33,6 @@ class ArgumentList:
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
@@ -68,11 +66,8 @@ class ArgumentList:
(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");')
+ # lie blatantly; we don't handle this case
+ self.variadic = False
# for a wrap function, the outer foo() wrapper will convert to a va_list,
# but the inner wrap_foo() just passes the va_list through.
@@ -212,6 +207,7 @@ class Function:
self.dirfd = 'AT_FDCWD'
self.flags = '0'
self.paths_to_munge = []
+ self.nowrappers = None
# used for the copyright date when creating stub functions
self.date = datetime.date.today().year
diff --git a/pseudo.1 b/pseudo.1
index 7002023..a9f37a6 100644
--- a/pseudo.1
+++ b/pseudo.1
@@ -400,6 +400,16 @@ in the
.I var/pseudo
directory, while clients log to standard error.
.TP 8
+.B PSEUDO_DISABLED
+If this variable is set to a value that doesn't look like f, F, n, N, or
+a numeric zero, the
+.I pseudo
+client library does not modify the behavior of called functions, though it
+continues to intercept them and block signals while processing them. This
+variable is reevaluated on every call to
+.IR fork(2) ,\ clone(2)
+or related functions.
+.TP 8
.B PSEUDO_ENOSYS_ABORT
If this variable is set, the
.I pseudo
diff --git a/pseudo.h b/pseudo.h
index 9b06651..38b663a 100644
--- a/pseudo.h
+++ b/pseudo.h
@@ -20,17 +20,16 @@
#include <stdlib.h>
#include <fcntl.h>
-struct pseudo_variables { char * key; size_t key_len; char * value; };
-
void pseudo_dump_env(char **envp);
-int pseudo_set_value(const char * key, const char * value);
-char * pseudo_get_value(const char * key);
+int pseudo_set_value(const char *key, const char *value);
+char *pseudo_get_value(const char *key);
#include "pseudo_tables.h"
extern void pseudo_debug_verbose(void);
extern void pseudo_debug_terse(void);
extern int pseudo_util_debug_fd;
+extern int pseudo_disabled;
#ifndef NDEBUG
extern int pseudo_debug_real(int, char *, ...) __attribute__ ((format (printf, 2, 3)));
#define pseudo_debug pseudo_debug_real
@@ -62,6 +61,9 @@ extern ssize_t pseudo_path_max(void);
extern int pseudo_etc_file(const char *filename, char *realname, int flags, char **search, int dircount);
#define PSEUDO_ETC_FILE(name, realname, flags) pseudo_etc_file((name), (realname), (flags), (char *[]) { pseudo_chroot, pseudo_passwd }, 2)
+/* refresh environment variables from internals */
+extern void pseudo_reinit_environment(void);
+
extern char *pseudo_version;
#ifndef PSEUDO_BINDIR
diff --git a/pseudo_client.c b/pseudo_client.c
index b169259..e30d3e7 100644
--- a/pseudo_client.c
+++ b/pseudo_client.c
@@ -58,6 +58,8 @@ char *pseudo_chroot = NULL;
char *pseudo_passwd = NULL;
size_t pseudo_chroot_len = 0;
char *pseudo_cwd_rel = NULL;
+/* used for PSEUDO_DISABLED */
+int pseudo_disabled = 0;
static char **fd_paths = NULL;
static int nfds = 0;
@@ -311,12 +313,50 @@ pseudo_client_close(int fd) {
void
pseudo_client_reset() {
+ char *env_disabled = NULL;
+
pseudo_antimagic();
pseudo_new_pid();
if (connect_fd != -1) {
close(connect_fd);
connect_fd = -1;
}
+
+ /* in child processes, PSEUDO_DISABLED may have become set to
+ * some truthy value, in which case we'd disable pseudo,
+ * or it may have gone away, in which case we'd enable
+ * pseudo.
+ */
+ env_disabled = getenv("PSEUDO_DISABLED");
+ if (env_disabled) {
+ int actually_disabled = 1;
+ switch (*env_disabled) {
+ case 'f':
+ case 'F':
+ case 'n':
+ case 'N':
+ actually_disabled = 0;
+ break;
+ case '0':
+ actually_disabled = atoi(env_disabled);
+ break;
+ }
+ if (actually_disabled) {
+ if (!pseudo_disabled) {
+ pseudo_antimagic();
+ pseudo_disabled = 1;
+ }
+ env_disabled = "1";
+ } else {
+ if (pseudo_disabled) {
+ pseudo_magic();
+ pseudo_disabled = 0;
+ }
+ env_disabled = "0";
+ }
+ pseudo_set_value("PSEUDO_DISABLED", env_disabled);
+ }
+
if (!pseudo_inited) {
char *env;
@@ -354,6 +394,8 @@ pseudo_client_reset() {
pseudo_inited = 1;
}
pseudo_client_getcwd();
+ /* make sure environment variables are back in sync */
+ pseudo_reinit_environment();
pseudo_magic();
}
diff --git a/pseudo_db.c b/pseudo_db.c
index 1a1b1a3..225dd37 100644
--- a/pseudo_db.c
+++ b/pseudo_db.c
@@ -820,7 +820,7 @@ pdb_query(char *stmt_type, pseudo_query_t *traits, unsigned long fields, int uni
sqlite3_stmt *stmt;
int done_any = 0;
int field = 0;
- char *order_by = "id";
+ const char *order_by = "id";
char *order_dir = "ASC";
int rc;
pseudo_query_field_t f;
diff --git a/pseudo_util.c b/pseudo_util.c
index 2e75e69..97c9911 100644
--- a/pseudo_util.c
+++ b/pseudo_util.c
@@ -39,6 +39,12 @@
#include "pseudo_ipc.h"
#include "pseudo_db.h"
+struct pseudo_variables {
+ char *key;
+ size_t key_len;
+ char *value;
+};
+
/* The order below is not arbitrary, but based on an assumption
* of how often things will be used.
*/
@@ -58,6 +64,7 @@ static struct pseudo_variables pseudo_env[] = {
{ "PSEUDO_ENOSYS_ABORT", 19, NULL },
{ "PSEUDO_NOSYMLINKEXP", 19, NULL },
{ "PSEUDO_RELOADED", 15, NULL },
+ { "PSEUDO_DISABLED", 15, NULL },
{ NULL, 0, NULL } /* Magic terminator */
};
@@ -69,12 +76,13 @@ static struct pseudo_variables pseudo_env[] = {
* program starts playing with things, so we need to do our
* best to handle that case.
*/
-int _in_init = -1; /* Not yet run */
+static int _pseudo_in_init = -1; /* Not yet run */
static void _libpseudo_init(void) __attribute__ ((constructor));
#if 0
-static void dump_env(char **envp) {
+static void
+dump_env(char **envp) {
size_t i = 0;
for (i = 0; envp[i]; i++) {
pseudo_debug(0,"dump_envp: [%d]%s\n", (int) i, envp[i]);
@@ -84,18 +92,27 @@ static void dump_env(char **envp) {
pseudo_debug(0,"dump_envp: {%d}%s=%s\n", (int) i, pseudo_env[i].key, pseudo_env[i].value);
}
- pseudo_debug(0, "dump_envp: _in_init %d\n", _in_init);
+ pseudo_debug(0, "dump_envp: _in_init %d\n", _pseudo_in_init);
}
#endif
+void
+pseudo_reinit_environment(void) {
+ _pseudo_in_init = 0;
+ _libpseudo_init();
+}
+
/* Caller must free memory! */
-char * pseudo_get_value(const char * key) {
+char *
+pseudo_get_value(const char *key) {
size_t i = 0;
char * value;
- if (_in_init == -1) _libpseudo_init();
+ if (_pseudo_in_init == -1)
+ _libpseudo_init();
- for (i = 0; pseudo_env[i].key && memcmp(pseudo_env[i].key, key, pseudo_env[i].key_len + 1); i++) ;
+ for (i = 0; pseudo_env[i].key && memcmp(pseudo_env[i].key, key, pseudo_env[i].key_len + 1); i++)
+ ;
/* Check if the environment has it and we don't ...
* if so, something went wrong... so we'll attempt to recover
@@ -103,8 +120,10 @@ char * pseudo_get_value(const char * key) {
if (pseudo_env[i].key && !pseudo_env[i].value && getenv(pseudo_env[i].key))
_libpseudo_init();
- if (pseudo_env[i].value) value = strdup(pseudo_env[i].value);
- else value = NULL;
+ if (pseudo_env[i].value)
+ value = strdup(pseudo_env[i].value);
+ else
+ value = NULL;
if (!pseudo_env[i].key)
pseudo_diag("Unknown variable %s.\n", key);
@@ -113,17 +132,20 @@ char * pseudo_get_value(const char * key) {
}
/* We make a copy, so the original values should be freed. */
-int pseudo_set_value(const char * key, const char * value) {
+int
+pseudo_set_value(const char *key, const char *value) {
int rc = 0;
size_t i = 0;
- if (_in_init == -1) _libpseudo_init();
+ if (_pseudo_in_init == -1)
+ _libpseudo_init();
for (i = 0; pseudo_env[i].key && memcmp(pseudo_env[i].key, key, pseudo_env[i].key_len + 1); i++)
;
if (pseudo_env[i].key) {
- if (pseudo_env[i].value) free(pseudo_env[i].value);
+ if (pseudo_env[i].value)
+ free(pseudo_env[i].value);
if (value) {
char *new = strdup(value);
if (new)
@@ -134,25 +156,25 @@ int pseudo_set_value(const char * key, const char * value) {
} else
pseudo_env[i].value = NULL;
} else {
- if (!_in_init) pseudo_diag("Unknown variable %s.\n", key);
+ if (!_pseudo_in_init) pseudo_diag("Unknown variable %s.\n", key);
rc = -EINVAL;
}
return rc;
}
-static void _libpseudo_init(void) {
+static void
+_libpseudo_init(void) {
size_t i = 0;
- _in_init = 1;
+ _pseudo_in_init = 1;
for (i = 0; pseudo_env[i].key; i++) {
- if (pseudo_env[i].key)
- if (getenv(pseudo_env[i].key))
- pseudo_set_value(pseudo_env[i].key, getenv(pseudo_env[i].key));
+ if (getenv(pseudo_env[i].key))
+ pseudo_set_value(pseudo_env[i].key, getenv(pseudo_env[i].key));
}
- _in_init = 0;
+ _pseudo_in_init = 0;
}
/* 5 = ridiculous levels of duplication
diff --git a/pseudo_wrappers.c b/pseudo_wrappers.c
index dbcfac8..0321fe2 100644
--- a/pseudo_wrappers.c
+++ b/pseudo_wrappers.c
@@ -246,3 +246,353 @@ pseudo_populate_wrappers(void) {
return done;
}
+static char **
+execl_to_v(va_list ap, const char *argv0, char *const **envp) {
+ size_t i = 0;
+ size_t alloc_size = 256;
+
+ char **argv = malloc((sizeof *argv) * alloc_size);
+
+ if (!argv) {
+ pseudo_debug(1, "execl failed: couldn't allocate memory for %lu arguments\n",
+ (unsigned long) alloc_size);
+ return NULL;
+ }
+ argv[i++] = (char *) argv0;
+
+ while (argv[i-1]) {
+ argv[i++] = va_arg(ap, char *const);
+ if (i > alloc_size - 1) {
+ alloc_size = alloc_size + 256;
+ argv = realloc(argv, (sizeof *argv) * alloc_size);
+ if (!argv) {
+ pseudo_debug(1, "execl failed: couldn't allocate memory for %lu arguments\n",
+ (unsigned long) alloc_size);
+ return NULL;
+ }
+ }
+ }
+ if (envp) {
+ *envp = va_arg(ap, char **);
+ }
+ return argv;
+}
+
+/* The following wrappers require Special Handling */
+
+int
+execl(const char *file, const char *arg, ...) {
+ sigset_t saved;
+ va_list ap;
+ char **argv;
+
+ int rc = -1;
+
+ va_start(ap, arg);
+ argv = execl_to_v(ap, arg, 0);
+ va_end(ap);
+ if (!argv) {
+ errno = ENOMEM;
+ return -1;
+ }
+
+ pseudo_debug(4, "called: execl\n");
+ pseudo_sigblock(&saved);
+ if (pseudo_getlock()) {
+ errno = EBUSY;
+ sigprocmask(SIG_SETMASK, &saved, NULL);
+ return -1;
+ }
+ if (pseudo_populate_wrappers()) {
+ int save_errno;
+ if (antimagic > 0) {
+ if (real_execv) {
+ /* use execv to emulate */
+ rc = (*real_execv)(file, argv);
+ } else {
+ /* rc was initialized to the "failure" value */
+ pseudo_enosys("execl");
+ }
+ } else {
+
+ /* exec*() use this to restore the sig mask */
+ pseudo_saved_sigmask = saved;
+ rc = wrap_execv(file, argv);
+ }
+
+ save_errno = errno;
+ pseudo_droplock();
+ sigprocmask(SIG_SETMASK, &saved, NULL);
+ pseudo_debug(4, "completed: execl\n");
+ errno = save_errno;
+ free(argv);
+ return rc;
+ } else {
+ pseudo_droplock();
+ sigprocmask(SIG_SETMASK, &saved, NULL);
+ pseudo_debug(4, "completed: execl\n");
+ /* rc was initialized to the "failure" value */
+ pseudo_enosys("execl");
+ free(argv);
+ return rc;
+ }
+}
+
+int
+execlp(const char *file, const char *arg, ...) {
+ sigset_t saved;
+ va_list ap;
+ char **argv;
+
+ int rc = -1;
+
+ va_start(ap, arg);
+ argv = execl_to_v(ap, arg, 0);
+ va_end(ap);
+ if (!argv) {
+ errno = ENOMEM;
+ return -1;
+ }
+
+ pseudo_debug(4, "called: execlp\n");
+ pseudo_sigblock(&saved);
+ if (pseudo_getlock()) {
+ errno = EBUSY;
+ sigprocmask(SIG_SETMASK, &saved, NULL);
+ return -1;
+ }
+ if (pseudo_populate_wrappers()) {
+ int save_errno;
+ if (antimagic > 0) {
+ if (real_execvp) {
+ /* use execv to emulate */
+ rc = (*real_execvp)(file, argv);
+ } else {
+ /* rc was initialized to the "failure" value */
+ pseudo_enosys("execlp");
+ }
+ } else {
+
+ /* exec*() use this to restore the sig mask */
+ pseudo_saved_sigmask = saved;
+ rc = wrap_execvp(file, argv);
+ }
+
+ save_errno = errno;
+ pseudo_droplock();
+ sigprocmask(SIG_SETMASK, &saved, NULL);
+ pseudo_debug(4, "completed: execlp\n");
+ errno = save_errno;
+ free(argv);
+ return rc;
+ } else {
+ pseudo_droplock();
+ sigprocmask(SIG_SETMASK, &saved, NULL);
+ pseudo_debug(4, "completed: execlp\n");
+ /* rc was initialized to the "failure" value */
+ pseudo_enosys("execlp");
+ free(argv);
+ return rc;
+ }
+}
+
+int
+execle(const char *file, const char *arg, ...) {
+ sigset_t saved;
+ va_list ap;
+ char **argv;
+ char **envp;
+
+ int rc = -1;
+
+ va_start(ap, arg);
+ argv = execl_to_v(ap, arg, (char *const **)&envp);
+ va_end(ap);
+ if (!argv) {
+ errno = ENOMEM;
+ return -1;
+ }
+
+ pseudo_debug(4, "called: execle\n");
+ pseudo_sigblock(&saved);
+ if (pseudo_getlock()) {
+ errno = EBUSY;
+ sigprocmask(SIG_SETMASK, &saved, NULL);
+ return -1;
+ }
+ if (pseudo_populate_wrappers()) {
+ int save_errno;
+ if (antimagic > 0) {
+ if (real_execve) {
+ /* use execve to emulate */
+ rc = (*real_execve)(file, argv, envp);
+ } else {
+ /* rc was initialized to the "failure" value */
+ pseudo_enosys("execl");
+ }
+ } else {
+
+ /* exec*() use this to restore the sig mask */
+ pseudo_saved_sigmask = saved;
+ rc = wrap_execve(file, argv, envp);
+ }
+
+ save_errno = errno;
+ pseudo_droplock();
+ sigprocmask(SIG_SETMASK, &saved, NULL);
+ pseudo_debug(4, "completed: execle\n");
+ errno = save_errno;
+ free(argv);
+ return rc;
+ } else {
+ pseudo_droplock();
+ sigprocmask(SIG_SETMASK, &saved, NULL);
+ pseudo_debug(4, "completed: execle\n");
+ /* rc was initialized to the "failure" value */
+ pseudo_enosys("execle");
+ free(argv);
+ return rc;
+ }
+}
+
+int
+fork(void) {
+ sigset_t saved;
+
+ int rc = -1;
+
+ pseudo_debug(4, "called: fork\n");
+ pseudo_sigblock(&saved);
+ if (pseudo_getlock()) {
+ errno = EBUSY;
+ sigprocmask(SIG_SETMASK, &saved, NULL);
+ return -1;
+ }
+ if (pseudo_populate_wrappers()) {
+ int save_errno;
+
+ rc = wrap_fork();
+
+ save_errno = errno;
+
+ pseudo_droplock();
+ sigprocmask(SIG_SETMASK, &saved, NULL);
+ pseudo_debug(4, "completed: fork\n");
+ errno = save_errno;
+ return rc;
+ } else {
+ pseudo_droplock();
+ sigprocmask(SIG_SETMASK, &saved, NULL);
+ pseudo_debug(4, "completed: fork\n");
+ /* rc was initialized to the "failure" value */
+ pseudo_enosys("fork");
+
+ return rc;
+ }
+}
+
+int
+vfork(void) {
+ /* we don't provide support for the distinct semantics
+ * of vfork()
+ */
+ return fork();
+}
+
+int
+wrap_clone(int (*fn)(void *), void *child_stack, int flags, void *arg, ...) {
+ /* unused */
+ return 0;
+}
+
+int
+clone(int (*fn)(void *), void *child_stack, int flags, void *arg, ...) {
+ sigset_t saved;
+ va_list ap;
+ pid_t *pid;
+ struct user_desc *tls;
+ pid_t *ctid;
+
+ int rc = -1;
+
+ va_start(ap, arg);
+ pid = va_arg(ap, pid_t *);
+ tls = va_arg(ap, struct user_desc *);
+ ctid = va_arg(ap, pid_t *);
+ va_end(ap);
+
+
+ pseudo_debug(4, "called: clone\n");
+ pseudo_sigblock(&saved);
+ if (pseudo_getlock()) {
+ errno = EBUSY;
+ sigprocmask(SIG_SETMASK, &saved, NULL);
+ return -1;
+ }
+ if (pseudo_populate_wrappers()) {
+ int save_errno;
+ int save_disabled = pseudo_disabled;
+ /* because clone() doesn't actually continue in this function, we
+ * can't check the return and fix up environment variables in the
+ * child. Instead, we have to temporarily do any fixup, then possibly
+ * undo it later. UGH!
+ */
+ pseudo_debug(1, "client resetting for clone(2) call\n");
+ if (real_clone) {
+ pseudo_setupenv();
+ pseudo_client_reset();
+ /* call the real syscall */
+ rc = (*real_clone)(fn, child_stack, flags, arg, pid, tls, ctid);
+
+ /* if we got here, we're the parent process. And if we changed
+ * pseudo_disabled because of the environment, now we want to
+ * bring it back. We can't use the normal path for this in
+ * pseudo_client_reset() because that would trust the environment
+ * variable, which was intended only to modify the behavior of
+ * the child process.
+ */
+ if (save_disabled != pseudo_disabled) {
+ if (pseudo_disabled) {
+ pseudo_disabled = 0;
+ pseudo_magic();
+ } else {
+ pseudo_disabled = 1;
+ pseudo_antimagic();
+ }
+ }
+ } else {
+ /* rc was initialized to the "failure" value */
+ pseudo_enosys("clone");
+ }
+
+ save_errno = errno;
+ pseudo_droplock();
+ sigprocmask(SIG_SETMASK, &saved, NULL);
+ pseudo_debug(4, "completed: clone\n");
+ errno = save_errno;
+ return rc;
+ } else {
+ pseudo_droplock();
+ sigprocmask(SIG_SETMASK, &saved, NULL);
+ pseudo_debug(4, "completed: clone\n");
+ /* rc was initialized to the "failure" value */
+ pseudo_enosys("clone");
+
+ return rc;
+ }
+}
+
+static int (*real_fork)(void) = NULL;
+static int (*real_execlp)(const char *file, const char *arg, ...) = NULL;
+static int (*real_execl)(const char *file, const char *arg, ...) = NULL;
+static int (*real_execle)(const char *file, const char *arg, ...) = NULL;
+static int (*real_clone)(int (*)(void *), void *, int, void *, ...) = NULL;
+
+static int
+wrap_fork(void) {
+ int rc = -1;
+
+#include "guts/fork.c"
+
+ return rc;
+}
diff --git a/templates/wrapfuncs.c b/templates/wrapfuncs.c
index 935561b..c0170d9 100644
--- a/templates/wrapfuncs.c
+++ b/templates/wrapfuncs.c
@@ -34,7 +34,7 @@ ${name}(${decl_args}) {
int save_errno;
if (antimagic > 0) {
if (real_$name) {
- ${prologue_call_real}
+ /* call the real syscall */
${rc_assign} (*real_${name})(${call_args});
} else {
/* rc was initialized to the "failure" value */
diff --git a/templates/wrapfuncs.h b/templates/wrapfuncs.h
index 63ba903..8088d09 100644
--- a/templates/wrapfuncs.h
+++ b/templates/wrapfuncs.h
@@ -8,3 +8,11 @@
/* ${comment} */
static ${type} wrap_${name}(${wrap_args});
static ${type} (*real_${name})(${decl_args});
+@footer
+/* special cases: functions with manually-written wrappers */
+
+/* int fork(void) */
+static int wrap_fork(void);
+static int (*real_fork)(void);
+static int wrap_clone(int (*fn)(void *), void *child_stack, int flags, void *arg, ...);
+static int (*real_clone)(int (*fn)(void *), void *child_stack, int flags, void *arg, ...);
diff --git a/templates/wrapper_table b/templates/wrapper_table
index a16dca9..f1a3220 100644
--- a/templates/wrapper_table
+++ b/templates/wrapper_table
@@ -16,5 +16,16 @@ static struct {
(int (*)(void)) wrap_${name}
},
@footer
+ /* special cases: Functions which need manually-coded wrappers */
+ { /* int fork(void); */
+ "fork",
+ (int (**)(void)) &real_fork,
+ (int (*)(void)) wrap_fork
+ },
+ { /* int clone(int (*fn)(void *), void *, int, void, ...); */
+ "clone",
+ (int (**)(void)) &real_clone,
+ (int (*)(void)) wrap_clone
+ },
{ NULL, NULL, NULL },
};
diff --git a/test/test-forkoff-env_i.sh b/test/test-forkoff-env_i.sh
new file mode 100755
index 0000000..1e679aa
--- /dev/null
+++ b/test/test-forkoff-env_i.sh
@@ -0,0 +1,30 @@
+#!/bin/bash
+
+# Verify normal operation...
+uid=`env -i id -u`
+gid=`env -i id -g`
+if [ $uid -ne 0 -o $gid -ne 0 ]; then
+ exit 1
+fi
+
+export PSEUDO_DISABLED=1
+# Verify we dropped OUT of pseudo control, even with env -i
+# This checks that env -i replacement functionality still works
+# as expected
+uid=`env -i id -u`
+gid=`env -i id -g`
+if [ $uid -eq 0 -o $gid -eq 0 ]; then
+ exit 1
+fi
+
+export PSEUDO_DISABLED=1
+# Verify we can change PSEUDO_DISABLED, even with env -i
+# This checks that env -i replacement functionality still works
+# as expected
+uid=`env -i PSEUDO_DISABLED=0 id -u`
+gid=`env -i PSEUDO_DISABLED=0 id -g`
+if [ $uid -ne 0 -o $gid -ne 0 ]; then
+ exit 1
+fi
+
+exit 0
diff --git a/test/test-forkoff.sh b/test/test-forkoff.sh
new file mode 100755
index 0000000..13a42a4
--- /dev/null
+++ b/test/test-forkoff.sh
@@ -0,0 +1,18 @@
+#!/bin/bash
+
+# Verify normal operation...
+uid=`id -u`
+gid=`id -g`
+if [ $uid -ne 0 -o $gid -ne 0 ]; then
+ exit 1
+fi
+
+export PSEUDO_DISABLED=1
+# Verify we dropped OUT of pseudo control
+uid=`id -u`
+gid=`id -g`
+if [ $uid -eq 0 -o $gid -eq 0 ]; then
+ exit 1
+fi
+
+exit 0
diff --git a/wrapfuncs.in b/wrapfuncs.in
index 17742ac..eb9280e 100644
--- a/wrapfuncs.in
+++ b/wrapfuncs.in
@@ -32,8 +32,6 @@ int rmdir(const char *path); /* flags=AT_SYMLINK_NOFOLLOW */
int chdir(const char *path);
int fchdir(int dirfd);
int fcntl(int fd, int cmd, ...{struct flock *lock});
-int fork(void);
-int vfork(void);
# just so we know the inums of symlinks
int symlink(const char *oldname, const char *newpath); /* flags=AT_SYMLINK_NOFOLLOW */
int symlinkat(const char *oldname, int dirfd, const char *newpath); /* flags=AT_SYMLINK_NOFOLLOW */
@@ -88,9 +86,6 @@ char *tmpnam(char *s);
int truncate(const char *path, off_t length);
int utime(const char *path, const struct utimbuf *buf);
int utimes(const char *path, const struct timeval *times);
-int execl(const char *file, const char *arg, ...);
-int execlp(const char *file, const char *arg, ...);
-int execle(const char *file, const char *arg, ...);
int execv(const char *file, char *const *argv);
int execve(const char *file, char *const *argv, char *const *envp);
int execvp(const char *file, char *const *argv);