2 * Copyright 2010, Sine Nomine Associates and others.
5 * This software has been released under the terms of the IBM Public
6 * License. For details, see the LICENSE file in the top-level source
7 * directory or online at http://www.openafs.org/dl/license10.html
10 #include <afsconfig.h>
11 #include <afs/param.h>
13 #include <afs/afsutil.h>
14 #include <tests/tap/basic.h>
17 #include <sys/types.h>
25 #define FAILSTR "exec test failure\n"
26 #define ARGSTRING "teststring"
27 #define PSTR(s) ((s) != NULL ? (s) : "(null)")
29 static struct exec_test
{
30 const char *prefix
; /* program prefix to run */
31 const char *suffix
; /* program suffix to run */
32 const char *result
; /* expected output from stdout from the child program,
33 or NULL if we should fail to run anything */
35 { "exec-test1.", NULL
, "exec test 1\n" },
36 { NULL
, ".exec-test2", "exec test 2\n" },
37 { "exec-test3.", ".suffix", "exec test 3\n" },
39 { "nonexistent", NULL
, NULL
},
43 create_child_script(const char *argv0
, struct exec_test
*test
)
46 char *dirc
, *basec
, *bname
, *dname
;
47 const char *prefix
, *suffix
;
56 basec
= strdup(argv0
);
57 script
= malloc(PATH_MAX
);
59 if (!dirc
|| !basec
|| !script
) {
63 dname
= dirname(dirc
);
64 bname
= basename(basec
);
66 prefix
= test
->prefix
;
70 suffix
= test
->suffix
;
75 sprintf(script
, "%s/%s%s%s", dname
, prefix
, bname
, suffix
);
80 fd
= open(script
, O_WRONLY
| O_CREAT
| O_EXCL
, 0770);
82 sysbail("open(%s)", script
);
90 fprintf(fh
, "#!/bin/sh\n"
91 "if test x\"$1\" = x%s ; then\n"
95 "fi\n", ARGSTRING
, test
->result
);
103 main(int argc
, char **argv
)
111 unsigned long nTests
= sizeof(tests
) / sizeof(tests
[0]);
114 /* in case afs_exec_alt tries to run us again */
118 child_argv
[0] = argv
[0];
119 child_argv
[1] = ARGSTRING
;
120 child_argv
[2] = NULL
;
125 * Testing afs_exec_alt is a little interesting, since a successful run
126 * means that we exec() someone else. Our general strategy is to fork and
127 * run some generated shell scripts that just output a (known) string and
128 * exit successfully. We have each of those scripts output something
129 * different so we can make sure we're executing the right one.
131 * This isn't 100% foolproof, since afs_exec_alt could bug out and exec
132 * some entirely different program, which happens to have the exact same
133 * behavior as our shell scripts. But we've tried to make that unlikely.
135 * The shell scripts are passed exactly one argument: 'teststring'. All
136 * they should do is see if that were given that argument, and if so,
137 * they should print out the expected 'result' string to stdout, and
138 * should exit with successful status.
141 for (i
= 0; i
< nTests
; i
++) {
151 child_script
= create_child_script(argv
[0], t
);
162 printf("child: close(%d) failed: %s", fds
[0], strerror(errno
));
166 if (dup2(fds
[1], 1) < 0) {
167 printf("dup2: %s", strerror(errno
));
171 prog
= afs_exec_alt(child_argc
, child_argv
, t
->prefix
, t
->suffix
);
172 if (t
->result
== NULL
) {
173 printf("%s", FAILSTR
);
176 printf("afs_exec_alt failed to exec %s: %s", prog
, strerror(errno
));
182 ssize_t result_len
, nBytes
;
187 sysdiag("parent: close(%d) failed", fds
[1]);
195 result_len
= strlen(result
);
197 nBytes
= read(fds
[0], buf
, sizeof(buf
)-1);
198 is_int(result_len
, nBytes
,
199 "child output size for prefix=%s, suffix=%s",
200 PSTR(t
->prefix
), PSTR(t
->suffix
));
203 skip("read() failed; cannot test read buffer");
205 /* just so is_string doesn't go running off into memory... */
208 is_string(result
, buf
,
209 "child output for prefix=%s, suffix=%s",
210 PSTR(t
->prefix
), PSTR(t
->suffix
));
214 sysdiag("parent: close(%d) failed", fds
[0]);
217 if (waitpid(pid
, &status
, 0) <= 0) {
222 if (unlink(child_script
)) {
223 sysdiag("unlink(%s)", child_script
);
228 ok(WIFEXITED(status
), "child exited for prefix=%s, suffix=%s",
229 PSTR(t
->prefix
), PSTR(t
->suffix
));
231 if (WIFEXITED(status
)) {
232 is_int(0, WEXITSTATUS(status
),
233 "child exit code for prefix=%s, suffix=%s",
234 PSTR(t
->prefix
), PSTR(t
->suffix
));
236 skip("!WIFEXITED(status) (status=%d), cannot check exit code",
238 if (WIFSIGNALED(status
)) {
239 diag("terminated with signal %d", WTERMSIG(status
));