Util: include assert.h in pthreads_nosig.h when required
[openafs.git] / src / util / exec.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 /* exec-related helper routines */
11
12 #include <afsconfig.h>
13 #include <afs/param.h>
14
15 #include <unistd.h>
16 #include <string.h>
17 #include <errno.h>
18 #include <stdlib.h>
19
20 /* helper for afs_exec_alt; just constructs the string for the 'alternate'
21  * program */
22 static char *
23 construct_alt(const char *argv0, const char *prefix, const char *suffix)
24 {
25     size_t prefix_len = strlen(prefix);
26     size_t suffix_len = strlen(suffix);
27     size_t replace_len;
28     char *alt, *replace;
29
30     alt = malloc(strlen(argv0) + prefix_len + suffix_len + 1);
31     if (!alt) {
32         return NULL;
33     }
34     strcpy(alt, argv0);
35
36     replace = strrchr(alt, '/');
37     if (replace) {
38         replace++;
39     } else {
40         replace = alt;
41     }
42
43     replace_len = strlen(replace);
44
45     memmove(replace + prefix_len, replace, replace_len);
46     memcpy(replace, prefix, prefix_len);
47     /* basically strcat(replace, suffix), but a touch faster */
48     memcpy(replace + prefix_len + replace_len, suffix, suffix_len);
49
50     return alt;
51 }
52
53 /**
54  * execute an alternate version of the running program.
55  *
56  * This takes the current argc/argv, and executes an "anternate" version
57  * of the current program ("alternate" meaning e.g. DAFS/non-DAFS,
58  * 32-bit/64-bit, etc). For example, if the current running program is
59  * fssync-debug, running afs_exec_alt(argc, argc, "da", NULL) will try
60  * to execute a "dafssync-debug" program, if it can be run.
61  *
62  * @param[in] argc  argc for the current program
63  * @param[in] argv  argv for the current program
64  * @param[in] prefix  prefix for the alternate program (or NULL)
65  * @param[in] suffix  suffix for the alternate program (or NULL)
66  *
67  * @return the program that we failed to run, or NULL if we failed
68  *         before constructing it. This string must be freed by the caller
69  *
70  * @pre at least one of 'prefix' or 'suffix' is non-NULL and non-empty
71  *
72  * @note if successful, never returns
73  */
74 char *
75 afs_exec_alt(int argc, char **argv, const char *prefix, const char *suffix)
76 {
77     char **targv = NULL;
78     char *ret = NULL;
79     int errno_save;
80     int i = 0;
81
82     if (!prefix) {
83         prefix = "";
84     }
85     if (!suffix) {
86         suffix = "";
87     }
88
89     if (prefix[0] == '\0' && suffix[0] == '\0') {
90         /* we'd be exec'ing ourselves; that's no good */
91         errno = EINVAL;
92         return NULL;
93     }
94
95     targv = malloc(sizeof(char*) * (argc+1));
96     if (!targv) {
97         goto error;
98     }
99
100     targv[0] = construct_alt(argv[0], prefix, suffix);
101     if (!targv[0]) {
102         goto error;
103     }
104
105     for (i = 1; i < argc; i++) {
106         targv[i] = strdup(argv[i]);
107         if (!targv[i]) {
108             goto error;
109         }
110     }
111     targv[i] = NULL;
112
113     execvp(targv[0], targv);
114
115  error:
116     /* we only call free() beyond this point, which I don't think would ever
117      * muck with errno, but I don't trust everyone's libc... */
118     errno_save = errno;
119     if (targv) {
120         i--;
121         for (; i >= 1; i--) {
122             free(targv[i]);
123         }
124         ret = targv[0];
125         free(targv);
126     }
127     errno = errno_save;
128     return ret;
129 }