mirror of
https://git.openafs.org/openafs.git
synced 2025-01-18 15:00:12 +00:00
2fe3a28c6e
Some libc implementations will crash when NULL string arguments are given to *printf. Avoid passing NULL string arguments in the make check tests that did so, and pass the string "(null)" instead. Change-Id: I65f11a3eef88d1c7b210c867ae0c40018160f55a Reviewed-on: https://gerrit.openafs.org/12377 Reviewed-by: Benjamin Kaduk <kaduk@mit.edu> Tested-by: BuildBot <buildbot@rampaginggeek.com>
246 lines
5.8 KiB
C
246 lines
5.8 KiB
C
/*
|
|
* Copyright 2010, Sine Nomine Associates and others.
|
|
* All Rights Reserved.
|
|
*
|
|
* This software has been released under the terms of the IBM Public
|
|
* License. For details, see the LICENSE file in the top-level source
|
|
* directory or online at http://www.openafs.org/dl/license10.html
|
|
*/
|
|
|
|
#include <afsconfig.h>
|
|
#include <afs/param.h>
|
|
|
|
#include <afs/afsutil.h>
|
|
#include <tests/tap/basic.h>
|
|
|
|
#include <unistd.h>
|
|
#include <sys/types.h>
|
|
#include <sys/wait.h>
|
|
#include <stdlib.h>
|
|
#include <errno.h>
|
|
#include <libgen.h>
|
|
#include <sys/stat.h>
|
|
#include <fcntl.h>
|
|
|
|
#define FAILSTR "exec test failure\n"
|
|
#define ARGSTRING "teststring"
|
|
#define PSTR(s) ((s) != NULL ? (s) : "(null)")
|
|
|
|
static struct exec_test {
|
|
const char *prefix; /* program prefix to run */
|
|
const char *suffix; /* program suffix to run */
|
|
const char *result; /* expected output from stdout from the child program,
|
|
or NULL if we should fail to run anything */
|
|
} tests[] = {
|
|
{ "exec-test1.", NULL, "exec test 1\n" },
|
|
{ NULL, ".exec-test2", "exec test 2\n" },
|
|
{ "exec-test3.", ".suffix", "exec test 3\n" },
|
|
{ NULL, NULL, NULL },
|
|
{ "nonexistent", NULL, NULL },
|
|
};
|
|
|
|
static char*
|
|
create_child_script(const char *argv0, struct exec_test *test)
|
|
{
|
|
char *script;
|
|
char *dirc, *basec, *bname, *dname;
|
|
const char *prefix, *suffix;
|
|
int fd;
|
|
FILE *fh;
|
|
|
|
if (!test->result) {
|
|
return NULL;
|
|
}
|
|
|
|
dirc = strdup(argv0);
|
|
basec = strdup(argv0);
|
|
script = malloc(PATH_MAX);
|
|
|
|
if (!dirc || !basec || !script) {
|
|
sysbail("malloc");
|
|
}
|
|
|
|
dname = dirname(dirc);
|
|
bname = basename(basec);
|
|
|
|
prefix = test->prefix;
|
|
if (!prefix) {
|
|
prefix = "";
|
|
}
|
|
suffix = test->suffix;
|
|
if (!suffix) {
|
|
suffix = "";
|
|
}
|
|
|
|
sprintf(script, "%s/%s%s%s", dname, prefix, bname, suffix);
|
|
|
|
free(dirc);
|
|
free(basec);
|
|
|
|
fd = open(script, O_WRONLY | O_CREAT | O_EXCL, 0770);
|
|
if (fd < 0) {
|
|
sysbail("open(%s)", script);
|
|
}
|
|
|
|
fh = fdopen(fd, "w");
|
|
if (!fh) {
|
|
sysbail("fdopen");
|
|
}
|
|
|
|
fprintf(fh, "#!/bin/sh\n"
|
|
"if test x\"$1\" = x%s ; then\n"
|
|
" echo %s\n"
|
|
"else\n"
|
|
" exit 1\n"
|
|
"fi\n", ARGSTRING, test->result);
|
|
|
|
fclose(fh);
|
|
|
|
return script;
|
|
}
|
|
|
|
int
|
|
main(int argc, char **argv)
|
|
{
|
|
int fds[2];
|
|
pid_t pid;
|
|
char *child_argv[3];
|
|
int child_argc = 2;
|
|
char buf[1024];
|
|
int i;
|
|
unsigned long nTests = sizeof(tests) / sizeof(tests[0]);
|
|
|
|
if (argc != 1) {
|
|
/* in case afs_exec_alt tries to run us again */
|
|
exit(1);
|
|
}
|
|
|
|
child_argv[0] = argv[0];
|
|
child_argv[1] = ARGSTRING;
|
|
child_argv[2] = NULL;
|
|
|
|
plan(nTests * 4);
|
|
|
|
/*
|
|
* Testing afs_exec_alt is a little interesting, since a successful run
|
|
* means that we exec() someone else. Our general strategy is to fork and
|
|
* run some generated shell scripts that just output a (known) string and
|
|
* exit successfully. We have each of those scripts output something
|
|
* different so we can make sure we're executing the right one.
|
|
*
|
|
* This isn't 100% foolproof, since afs_exec_alt could bug out and exec
|
|
* some entirely different program, which happens to have the exact same
|
|
* behavior as our shell scripts. But we've tried to make that unlikely.
|
|
*
|
|
* The shell scripts are passed exactly one argument: 'teststring'. All
|
|
* they should do is see if that were given that argument, and if so,
|
|
* they should print out the expected 'result' string to stdout, and
|
|
* should exit with successful status.
|
|
*/
|
|
|
|
for (i = 0; i < nTests; i++) {
|
|
char *child_script;
|
|
struct exec_test *t;
|
|
|
|
t = &tests[i];
|
|
|
|
if (pipe(fds)) {
|
|
sysbail("pipe");
|
|
}
|
|
|
|
child_script = create_child_script(argv[0], t);
|
|
|
|
pid = fork();
|
|
if (pid < 0) {
|
|
sysbail("fork");
|
|
}
|
|
|
|
if (pid == 0) {
|
|
char *prog;
|
|
|
|
if (close(fds[0])) {
|
|
printf("child: close(%d) failed: %s", fds[0], strerror(errno));
|
|
}
|
|
|
|
/* child */
|
|
if (dup2(fds[1], 1) < 0) {
|
|
printf("dup2: %s", strerror(errno));
|
|
exit(1);
|
|
}
|
|
|
|
prog = afs_exec_alt(child_argc, child_argv, t->prefix, t->suffix);
|
|
if (t->result == NULL) {
|
|
printf("%s", FAILSTR);
|
|
exit(0);
|
|
}
|
|
printf("afs_exec_alt failed to exec %s: %s", prog, strerror(errno));
|
|
exit(1);
|
|
|
|
} else {
|
|
/* parent */
|
|
|
|
ssize_t result_len, nBytes;
|
|
const char *result;
|
|
int status;
|
|
|
|
if (close(fds[1])) {
|
|
sysdiag("parent: close(%d) failed", fds[1]);
|
|
}
|
|
|
|
result = t->result;
|
|
if (!result) {
|
|
result = FAILSTR;
|
|
}
|
|
|
|
result_len = strlen(result);
|
|
|
|
nBytes = read(fds[0], buf, sizeof(buf)-1);
|
|
is_int(result_len, nBytes,
|
|
"child output size for prefix=%s, suffix=%s",
|
|
PSTR(t->prefix), PSTR(t->suffix));
|
|
|
|
if (nBytes < 0) {
|
|
skip("read() failed; cannot test read buffer");
|
|
} else {
|
|
/* just so is_string doesn't go running off into memory... */
|
|
buf[nBytes] = '\0';
|
|
|
|
is_string(result, buf,
|
|
"child output for prefix=%s, suffix=%s",
|
|
PSTR(t->prefix), PSTR(t->suffix));
|
|
}
|
|
|
|
if (close(fds[0])) {
|
|
sysdiag("parent: close(%d) failed", fds[0]);
|
|
}
|
|
|
|
if (waitpid(pid, &status, 0) <= 0) {
|
|
sysbail("waitpid");
|
|
}
|
|
|
|
if (child_script) {
|
|
if (unlink(child_script)) {
|
|
sysdiag("unlink(%s)", child_script);
|
|
}
|
|
free(child_script);
|
|
}
|
|
|
|
ok(WIFEXITED(status), "child exited for prefix=%s, suffix=%s",
|
|
PSTR(t->prefix), PSTR(t->suffix));
|
|
|
|
if (WIFEXITED(status)) {
|
|
is_int(0, WEXITSTATUS(status),
|
|
"child exit code for prefix=%s, suffix=%s",
|
|
PSTR(t->prefix), PSTR(t->suffix));
|
|
} else {
|
|
skip("!WIFEXITED(status) (status=%d), cannot check exit code",
|
|
status);
|
|
if (WIFSIGNALED(status)) {
|
|
diag("terminated with signal %d", WTERMSIG(status));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return 0;
|
|
}
|