Update NEWS for 1.8.0pre3
[pkg-k5-afs_openafs.git] / tests / util / exec-alt-t.c
blob07f3d2c38b1b50decf2dcf32987c9b672093a96d
1 /*
2 * Copyright 2010, Sine Nomine Associates and others.
3 * All Rights Reserved.
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
8 */
10 #include <afsconfig.h>
11 #include <afs/param.h>
13 #include <afs/afsutil.h>
14 #include <tests/tap/basic.h>
16 #include <unistd.h>
17 #include <sys/types.h>
18 #include <sys/wait.h>
19 #include <stdlib.h>
20 #include <errno.h>
21 #include <libgen.h>
22 #include <sys/stat.h>
23 #include <fcntl.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 */
34 } tests[] = {
35 { "exec-test1.", NULL, "exec test 1\n" },
36 { NULL, ".exec-test2", "exec test 2\n" },
37 { "exec-test3.", ".suffix", "exec test 3\n" },
38 { NULL, NULL, NULL },
39 { "nonexistent", NULL, NULL },
42 static char*
43 create_child_script(const char *argv0, struct exec_test *test)
45 char *script;
46 char *dirc, *basec, *bname, *dname;
47 const char *prefix, *suffix;
48 int fd;
49 FILE *fh;
51 if (!test->result) {
52 return NULL;
55 dirc = strdup(argv0);
56 basec = strdup(argv0);
57 script = malloc(PATH_MAX);
59 if (!dirc || !basec || !script) {
60 sysbail("malloc");
63 dname = dirname(dirc);
64 bname = basename(basec);
66 prefix = test->prefix;
67 if (!prefix) {
68 prefix = "";
70 suffix = test->suffix;
71 if (!suffix) {
72 suffix = "";
75 sprintf(script, "%s/%s%s%s", dname, prefix, bname, suffix);
77 free(dirc);
78 free(basec);
80 fd = open(script, O_WRONLY | O_CREAT | O_EXCL, 0770);
81 if (fd < 0) {
82 sysbail("open(%s)", script);
85 fh = fdopen(fd, "w");
86 if (!fh) {
87 sysbail("fdopen");
90 fprintf(fh, "#!/bin/sh\n"
91 "if test x\"$1\" = x%s ; then\n"
92 " echo %s\n"
93 "else\n"
94 " exit 1\n"
95 "fi\n", ARGSTRING, test->result);
97 fclose(fh);
99 return script;
103 main(int argc, char **argv)
105 int fds[2];
106 pid_t pid;
107 char *child_argv[3];
108 int child_argc = 2;
109 char buf[1024];
110 int i;
111 unsigned long nTests = sizeof(tests) / sizeof(tests[0]);
113 if (argc != 1) {
114 /* in case afs_exec_alt tries to run us again */
115 exit(1);
118 child_argv[0] = argv[0];
119 child_argv[1] = ARGSTRING;
120 child_argv[2] = NULL;
122 plan(nTests * 4);
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++) {
142 char *child_script;
143 struct exec_test *t;
145 t = &tests[i];
147 if (pipe(fds)) {
148 sysbail("pipe");
151 child_script = create_child_script(argv[0], t);
153 pid = fork();
154 if (pid < 0) {
155 sysbail("fork");
158 if (pid == 0) {
159 char *prog;
161 if (close(fds[0])) {
162 printf("child: close(%d) failed: %s", fds[0], strerror(errno));
165 /* child */
166 if (dup2(fds[1], 1) < 0) {
167 printf("dup2: %s", strerror(errno));
168 exit(1);
171 prog = afs_exec_alt(child_argc, child_argv, t->prefix, t->suffix);
172 if (t->result == NULL) {
173 printf("%s", FAILSTR);
174 exit(0);
176 printf("afs_exec_alt failed to exec %s: %s", prog, strerror(errno));
177 exit(1);
179 } else {
180 /* parent */
182 ssize_t result_len, nBytes;
183 const char *result;
184 int status;
186 if (close(fds[1])) {
187 sysdiag("parent: close(%d) failed", fds[1]);
190 result = t->result;
191 if (!result) {
192 result = FAILSTR;
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));
202 if (nBytes < 0) {
203 skip("read() failed; cannot test read buffer");
204 } else {
205 /* just so is_string doesn't go running off into memory... */
206 buf[nBytes] = '\0';
208 is_string(result, buf,
209 "child output for prefix=%s, suffix=%s",
210 PSTR(t->prefix), PSTR(t->suffix));
213 if (close(fds[0])) {
214 sysdiag("parent: close(%d) failed", fds[0]);
217 if (waitpid(pid, &status, 0) <= 0) {
218 sysbail("waitpid");
221 if (child_script) {
222 if (unlink(child_script)) {
223 sysdiag("unlink(%s)", child_script);
225 free(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));
235 } else {
236 skip("!WIFEXITED(status) (status=%d), cannot check exit code",
237 status);
238 if (WIFSIGNALED(status)) {
239 diag("terminated with signal %d", WTERMSIG(status));
244 return 0;