comerr: Convert to using libtool
[openafs.git] / tests / util / exec-alt-t.c
1 /*
2  * Copyright 2010, Sine Nomine Associates and others.
3  * All Rights Reserved.
4  *
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  */
9
10 #include <afsconfig.h>
11 #include <afs/param.h>
12
13 #include <afs/afsutil.h>
14 #include <tests/tap/basic.h>
15
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>
24
25 #define FAILSTR "exec test failure\n"
26 #define ARGSTRING "teststring"
27
28 static struct exec_test {
29     const char *prefix; /* program prefix to run */
30     const char *suffix; /* program suffix to run */
31     const char *result; /* expected output from stdout from the child program,
32                            or NULL if we should fail to run anything */
33 } tests[] = {
34     { "exec-test1.",      NULL,          "exec test 1\n" },
35     { NULL,               ".exec-test2", "exec test 2\n" },
36     { "exec-test3.",      ".suffix",     "exec test 3\n" },
37     { NULL,               NULL,          NULL },
38     { "nonexistent",      NULL,          NULL },
39 };
40
41 static char*
42 create_child_script(const char *argv0, struct exec_test *test)
43 {
44     char *script;
45     char *dirc, *basec, *bname, *dname;
46     const char *prefix, *suffix;
47     int fd;
48     FILE *fh;
49
50     if (!test->result) {
51         return NULL;
52     }
53
54     dirc = strdup(argv0);
55     basec = strdup(argv0);
56     script = malloc(PATH_MAX);
57
58     if (!dirc || !basec || !script) {
59         sysbail("malloc");
60     }
61
62     dname = dirname(dirc);
63     bname = basename(basec);
64
65     prefix = test->prefix;
66     if (!prefix) {
67         prefix = "";
68     }
69     suffix = test->suffix;
70     if (!suffix) {
71         suffix = "";
72     }
73
74     sprintf(script, "%s/%s%s%s", dname, prefix, bname, suffix);
75
76     free(dirc);
77     free(basec);
78
79     fd = open(script, O_WRONLY | O_CREAT | O_EXCL, 0770);
80     if (fd < 0) {
81         sysbail("open(%s)", script);
82     }
83
84     fh = fdopen(fd, "w");
85     if (!fh) {
86         sysbail("fdopen");
87     }
88
89     fprintf(fh, "#!/bin/sh\n"
90 "if test x\"$1\" = x%s ; then\n"
91 "    echo %s\n"
92 "else\n"
93 "    exit 1\n"
94 "fi\n", ARGSTRING, test->result);
95
96     fclose(fh);
97
98     return script;
99 }
100
101 int
102 main(int argc, char **argv)
103 {
104     int fds[2];
105     pid_t pid;
106     char *child_argv[3];
107     int child_argc = 2;
108     char buf[1024];
109     int i;
110     unsigned long nTests = sizeof(tests) / sizeof(tests[0]);
111
112     if (argc != 1) {
113         /* in case afs_exec_alt tries to run us again */
114         exit(1);
115     }
116
117     child_argv[0] = argv[0];
118     child_argv[1] = ARGSTRING;
119     child_argv[2] = NULL;
120
121     plan(nTests * 4);
122
123     /*
124      * Testing afs_exec_alt is a little interesting, since a successful run
125      * means that we exec() someone else. Our general strategy is to fork and
126      * run some generated shell scripts that just output a (known) string and
127      * exit successfully. We have each of those scripts output something
128      * different so we can make sure we're executing the right one.
129      *
130      * This isn't 100% foolproof, since afs_exec_alt could bug out and exec
131      * some entirely different program, which happens to have the exact same
132      * behavior as our shell scripts. But we've tried to make that unlikely.
133      *
134      * The shell scripts are passed exactly one argument: 'teststring'. All
135      * they should do is see if that were given that argument, and if so,
136      * they should print out the expected 'result' string to stdout, and
137      * should exit with successful status.
138      */
139
140     for (i = 0; i < nTests; i++) {
141         char *child_script;
142         struct exec_test *t;
143
144         t = &tests[i];
145
146         if (pipe(fds)) {
147             sysbail("pipe");
148         }
149
150         child_script = create_child_script(argv[0], t);
151
152         pid = fork();
153         if (pid < 0) {
154             sysbail("fork");
155         }
156
157         if (pid == 0) {
158             char *prog;
159
160             if (close(fds[0])) {
161                 printf("child: close(%d) failed: %s", fds[0], strerror(errno));
162             }
163
164             /* child */
165             if (dup2(fds[1], 1) < 0) {
166                 printf("dup2: %s", strerror(errno));
167                 exit(1);
168             }
169
170             prog = afs_exec_alt(child_argc, child_argv, t->prefix, t->suffix);
171             if (t->result == NULL) {
172                 printf("%s", FAILSTR);
173                 exit(0);
174             }
175             printf("afs_exec_alt failed to exec %s: %s", prog, strerror(errno));
176             exit(1);
177
178         } else {
179             /* parent */
180
181             ssize_t result_len, nBytes;
182             const char *result;
183             int status;
184
185             if (close(fds[1])) {
186                 sysdiag("parent: close(%d) failed", fds[1]);
187             }
188
189             result = t->result;
190             if (!result) {
191                 result = FAILSTR;
192             }
193
194             result_len = strlen(result);
195
196             nBytes = read(fds[0], buf, sizeof(buf)-1);
197             is_int(result_len, nBytes,
198                    "child output size for prefix=%s, suffix=%s",
199                    t->prefix, t->suffix);
200
201             if (nBytes < 0) {
202                 skip("read() failed; cannot test read buffer");
203             } else {
204                 /* just so is_string doesn't go running off into memory... */
205                 buf[nBytes] = '\0';
206
207                 is_string(result, buf,
208                           "child output for prefix=%s, suffix=%s",
209                           t->prefix, t->suffix);
210             }
211
212             if (close(fds[0])) {
213                 sysdiag("parent: close(%d) failed", fds[0]);
214             }
215
216             if (waitpid(pid, &status, 0) <= 0) {
217                 sysbail("waitpid");
218             }
219
220             if (child_script) {
221                 if (unlink(child_script)) {
222                     sysdiag("unlink(%s)", child_script);
223                 }
224                 free(child_script);
225             }
226
227             ok(WIFEXITED(status), "child exited for prefix=%s, suffix=%s",
228                                   t->prefix, t->suffix);
229
230             if (WIFEXITED(status)) {
231                 is_int(0, WEXITSTATUS(status),
232                        "child exit code for prefix=%s, suffix=%s",
233                        t->prefix, t->suffix);
234             } else {
235                 skip("!WIFEXITED(status) (status=%d), cannot check exit code",
236                      status);
237                 if (WIFSIGNALED(status)) {
238                     diag("terminated with signal %d", WTERMSIG(status));
239                 }
240             }
241         }
242     }
243     return 0;
244 }