Skip to content

Commit 240f0c4

Browse files
authored
Merge pull request #229 from bgamari/wip/T219
Fix #219
2 parents 614e408 + 9d0401e commit 240f0c4

File tree

4 files changed

+89
-22
lines changed

4 files changed

+89
-22
lines changed

cbits/posix/common.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,8 @@ struct std_handle {
2525
int get_max_fd(void);
2626

2727
// defined in find_executable.c
28-
#if !defined(HAVE_execvpe)
29-
char *find_executable(char *filename);
28+
#if !defined(HAVE_EXECVPE)
29+
char *find_executable(char *workingDirectory, char *filename);
3030
#endif
3131

3232
// defined in fork_exec.c
@@ -49,4 +49,4 @@ do_spawn_posix (char *const args[],
4949
struct std_handle *stdErrHdl,
5050
gid_t *childGroup, uid_t *childUser,
5151
int flags,
52-
char **failed_doing);
52+
char **failed_doing);

cbits/posix/find_executable.c

Lines changed: 74 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -12,18 +12,48 @@
1212
#include "common.h"
1313

1414
// the below is only necessary when we need to emulate execvpe.
15-
#if !defined(HAVE_execvpe)
15+
#if !defined(HAVE_EXECVPE)
1616

17-
/* Return true if the given file exists and is an executable. */
18-
static bool is_executable(const char *path) {
19-
return access(path, X_OK) == 0;
17+
/* A quick check for whether the given path is absolute. */
18+
static bool is_absolute(const char *path) {
19+
return path[0] == '/';
20+
}
21+
22+
static char *concat_paths(const char *path1, const char *path2) {
23+
if (is_absolute(path2)) {
24+
return strdup(path2);
25+
} else {
26+
int len = strlen(path1) + 1 + strlen(path2) + 1;
27+
char *tmp = malloc(len);
28+
int ret = snprintf(tmp, len, "%s/%s", path1, path2);
29+
if (ret < 0) {
30+
free(tmp);
31+
return NULL;
32+
}
33+
return tmp;
34+
}
35+
}
36+
37+
/* Return true if the given file exists and is an executable, optionally
38+
* relative to the given working directory.
39+
*/
40+
static bool is_executable(char *working_dir, const char *path) {
41+
if (working_dir && !is_absolute(path)) {
42+
char *tmp = concat_paths(working_dir, path);
43+
bool ret = access(tmp, X_OK) == 0;
44+
free(tmp);
45+
return ret;
46+
} else {
47+
return access(path, X_OK) == 0;
48+
}
2049
}
2150

2251
/* Find an executable with the given filename in the given search path. The
2352
* result must be freed by the caller. Returns NULL if a matching file is not
2453
* found.
2554
*/
26-
static char *find_in_search_path(char *search_path, const char *filename) {
55+
static char *find_in_search_path(char *working_dir, char *search_path, const char *filename) {
56+
int workdir_len = strlen(working_dir);
2757
const int filename_len = strlen(filename);
2858
char *tokbuf;
2959
char *path = strtok_r(search_path, ":", &tokbuf);
@@ -34,13 +64,21 @@ static char *find_in_search_path(char *search_path, const char *filename) {
3464
#pragma GCC diagnostic push
3565
#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
3666
#endif
37-
const int tmp_len = filename_len + 1 + strlen(path) + 1;
67+
char *tmp;
68+
if (is_absolute(path)) {
69+
const int tmp_len = strlen(path) + 1 + filename_len + 1;
70+
tmp = malloc(tmp_len);
71+
snprintf(tmp, tmp_len, "%s/%s", path, filename);
72+
} else {
73+
const int tmp_len = workdir_len + 1 + strlen(path) + 1 + filename_len + 1;
74+
tmp = malloc(tmp_len);
75+
snprintf(tmp, tmp_len, "%s/%s/%s", working_dir, path, filename);
76+
}
3877
#if defined(__GNUC__) && __GNUC__ == 6 && __GNUC_MINOR__ == 3
3978
#pragma GCC diagnostic pop
4079
#endif
41-
char *tmp = malloc(tmp_len);
42-
snprintf(tmp, tmp_len, "%s/%s", path, filename);
43-
if (is_executable(tmp)) {
80+
81+
if (is_executable(working_dir, tmp)) {
4482
return tmp;
4583
} else {
4684
free(tmp);
@@ -74,15 +112,36 @@ static char *get_executable_search_path(void) {
74112
return strdup(":");
75113
}
76114

77-
/* Find the given executable in the executable search path. */
78-
char *find_executable(char *filename) {
79-
/* If it's an absolute or relative path name, it's easy. */
80-
if (strchr(filename, '/') && is_executable(filename)) {
115+
/* Find the given executable in the executable search path relative to
116+
* workingDirectory (or the current directory, if NULL).
117+
* N.B. the caller is responsible for free()ing the result.
118+
*/
119+
char *find_executable(char *working_dir, char *filename) {
120+
/* Drop trailing slash from working directory if necessary */
121+
if (working_dir) {
122+
int workdir_len = strlen(working_dir);
123+
if (working_dir[workdir_len-1] == '/') {
124+
working_dir[workdir_len-1] = '\0';
125+
}
126+
}
127+
128+
if (is_absolute(filename)) {
129+
/* If it's an absolute path name, it's easy. */
81130
return filename;
131+
132+
} else if (strchr(filename, '/')) {
133+
/* If it's a relative path name, we must look for executables relative
134+
* to the working directory. */
135+
if (is_executable(working_dir, filename)) {
136+
return filename;
137+
}
82138
}
83139

140+
/* Otherwise look through the search path... */
84141
char *search_path = get_executable_search_path();
85-
return find_in_search_path(search_path, filename);
142+
char *result = find_in_search_path(working_dir, search_path, filename);
143+
free(search_path);
144+
return result;
86145
}
87146

88-
#endif
147+
#endif

cbits/posix/fork_exec.c

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
/* ensure that execvpe is provided if possible */
2+
#define _GNU_SOURCE 1
3+
14
#include "common.h"
25

36
#include <sys/types.h>
@@ -125,10 +128,15 @@ do_spawn_fork (char *const args[],
125128
// we emulate this using fork and exec. However, to safely do so
126129
// we need to perform all allocations *prior* to forking. Consequently, we
127130
// need to find_executable before forking.
128-
#if !defined(HAVE_execvpe)
131+
#if !defined(HAVE_EXECVPE)
129132
char *exec_path;
130133
if (environment) {
131-
exec_path = find_executable(args[0]);
134+
exec_path = find_executable(workingDirectory, args[0]);
135+
if (exec_path == NULL) {
136+
errno = -ENOENT;
137+
*failed_doing = "find_executable";
138+
return -1;
139+
}
132140
}
133141
#endif
134142

@@ -234,7 +242,7 @@ do_spawn_fork (char *const args[],
234242

235243
/* the child */
236244
if (environment) {
237-
#if defined(HAVE_execvpe)
245+
#if defined(HAVE_EXECVPE)
238246
// XXX Check result
239247
execvpe(args[0], args, environment);
240248
#else

cbits/posix/posix_spawn.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,7 @@ do_spawn_posix (char *const args[],
159159
#if defined(HAVE_POSIX_SPAWN_SETPGROUP)
160160
spawn_flags |= POSIX_SPAWN_SETPGROUP;
161161
#else
162-
goto not_supported;
162+
goto not_supported;
163163
#endif
164164
}
165165

0 commit comments

Comments
 (0)