Improve OS-dependent determination of file name for the current executable

In several places, the old code was limiting this file name to 256 characters at most.  At least one user is bothered by this restriction.

- Change API of caml_executable_name() to return a string dynamically allocated with caml_stat_alloc.
- Update implementation of caml_executable_name() byterun/unix.c and byterun/win32.c to handle file names of (almost) arbitrary length.
- Add special code for MacOS X, not tested yet.

Fun fact of the day: under (some versions of) Linux, lstat() on "/proc/self/exe" returns 0 as length, so we can't use it to determine the size of the name in advance.
master
Xavier Leroy 2016-09-05 08:49:25 +02:00
parent 1b1f5e2470
commit a91d6a5470
5 changed files with 72 additions and 35 deletions

View File

@ -102,8 +102,7 @@ extern void caml_install_invalid_parameter_handler();
void caml_main(char **argv)
{
char * exe_name;
static char proc_self_exe[256];
char * exe_name, * proc_self_exe;
value res;
char tos;
@ -133,11 +132,13 @@ void caml_main(char **argv)
caml_debugger_init (); /* force debugger.o stub to be linked */
exe_name = argv[0];
if (exe_name == NULL) exe_name = "";
if (caml_executable_name(proc_self_exe, sizeof(proc_self_exe)) == 0)
proc_self_exe = caml_executable_name();
if (proc_self_exe != NULL)
exe_name = proc_self_exe;
else
exe_name = caml_search_exe_in_path(exe_name);
caml_sys_init(exe_name, argv);
caml_stat_free(exe_name);
if (sigsetjmp(caml_termination_jmpbuf.buf, 0)) {
if (caml_termination_hook != NULL) caml_termination_hook(NULL);
return;

View File

@ -82,8 +82,9 @@ extern char * caml_dlerror(void);
extern int caml_read_directory(char * dirname, struct ext_table * contents);
/* Recover executable name if possible (/proc/sef/exe under Linux,
GetModuleFileName under Windows). */
extern int caml_executable_name(char * name, int name_len);
GetModuleFileName under Windows). Return NULL on error,
string allocated with [caml_stat_alloc] on success. */
extern char * caml_executable_name(void);
#endif /* CAML_INTERNALS */

View File

@ -97,11 +97,11 @@ int caml_attempt_open(char **name, struct exec_trailer *trail,
char buf [2];
truename = caml_search_exe_in_path(*name);
*name = truename;
caml_gc_message(0x100, "Opening bytecode executable %s\n",
(uintnat) truename);
fd = open(truename, O_RDONLY | O_BINARY);
if (fd == -1) {
caml_stat_free(truename);
caml_gc_message(0x100, "Cannot open file\n", 0);
return FILE_NOT_FOUND;
}
@ -109,6 +109,7 @@ int caml_attempt_open(char **name, struct exec_trailer *trail,
err = read (fd, buf, 2);
if (err < 2 || (buf [0] == '#' && buf [1] == '!')) {
close(fd);
caml_stat_free(truename);
caml_gc_message(0x100, "Rejected #! script\n", 0);
return BAD_BYTECODE;
}
@ -116,9 +117,11 @@ int caml_attempt_open(char **name, struct exec_trailer *trail,
err = read_trailer(fd, trail);
if (err != 0) {
close(fd);
caml_stat_free(truename);
caml_gc_message(0x100, "Not a bytecode executable\n", 0);
return err;
}
*name = truename;
return fd;
}
@ -279,8 +282,7 @@ CAMLexport void caml_main(char **argv)
struct channel * chan;
value res;
char * shared_lib_path, * shared_libs, * req_prims;
char * exe_name;
static char proc_self_exe[256];
char * exe_name, * proc_self_exe;
ensure_spacetime_dot_o_is_included++;
@ -310,10 +312,10 @@ CAMLexport void caml_main(char **argv)
/* Should we really do that at all? The current executable is ocamlrun
itself, it's never a bytecode program. */
if (fd < 0
&& caml_executable_name(proc_self_exe, sizeof(proc_self_exe)) == 0) {
if (fd < 0 && (proc_self_exe = caml_executable_name()) != NULL) {
exe_name = proc_self_exe;
fd = caml_attempt_open(&exe_name, &trail, 0);
caml_stat_free(proc_self_exe);
}
if (fd < 0) {
@ -400,7 +402,6 @@ CAMLexport void caml_startup_code(
value res;
char * cds_file;
char * exe_name;
static char proc_self_exe[256];
caml_init_ieee_floats();
#if defined(_MSC_VER) && __STDC_SECURE_LIB__ >= 200411L
@ -415,9 +416,8 @@ CAMLexport void caml_startup_code(
caml_cds_file = caml_strdup(cds_file);
}
caml_parse_ocamlrunparam();
exe_name = argv[0];
if (caml_executable_name(proc_self_exe, sizeof(proc_self_exe)) == 0)
exe_name = proc_self_exe;
exe_name = caml_executable_name();
if (exe_name == NULL) exe_name = caml_search_exe_in_path(argv[0]);
caml_external_raise = NULL;
/* Initialize the abstract machine */
caml_init_gc (caml_init_minor_heap_wsz, caml_init_heap_wsz,
@ -456,6 +456,7 @@ CAMLexport void caml_startup_code(
caml_section_table_size = section_table_size;
/* Initialize system libraries */
caml_sys_init(exe_name, argv);
caml_stat_free(exe_name);
/* Execute the program */
caml_debugger(PROGRAM_START);
res = caml_interprete(caml_start_code, caml_code_size);

View File

@ -43,6 +43,9 @@
#else
#include <sys/dir.h>
#endif
#ifdef __APPLE__
#include <mach-o/dyld.h>
#endif
#include "caml/fail.h"
#include "caml/memory.h"
#include "caml/misc.h"
@ -347,28 +350,50 @@ CAMLexport int caml_read_directory(char * dirname, struct ext_table * contents)
/* Recover executable name from /proc/self/exe if possible */
#ifdef __linux__
int caml_executable_name(char * name, int name_len)
char * caml_executable_name(void)
{
int retcode;
#if defined(__linux__)
int namelen, retcode;
char * name;
struct stat st;
retcode = readlink("/proc/self/exe", name, name_len);
if (retcode == -1 || retcode >= name_len) return -1;
/* lstat("/proc/self/exe") returns st_size == 0 so we cannot use it
to determine the size of the buffer. Instead, we guess and adjust. */
namelen = 256;
while (1) {
name = caml_stat_alloc(namelen + 1);
retcode = readlink("/proc/self/exe", name, namelen);
if (retcode == -1) { caml_stat_free(name); return NULL; }
if (retcode <= namelen) break;
caml_stat_free(name);
if (namelen >= 1024*1024) return NULL; /* avoid runaway and overflow */
namelen *= 2;
}
/* readlink() does not zero-terminate its result */
name[retcode] = 0;
/* Make sure that the contents of /proc/self/exe is a regular file.
(Old Linux kernels return an inode number instead.) */
if (stat(name, &st) != 0) return -1;
if (! S_ISREG(st.st_mode)) return -1;
return 0;
}
if (stat(name, &st) == -1 || ! S_ISREG(st.st_mode)) {
caml_stat_free(name); return NULL;
}
return name;
#elif defined(__APPLE__)
unsigned int namelen;
char * name;
namelen = 256;
name = caml_stat_alloc(namelen);
if (_NSGetExecutablePath(name, &namelen) == 0) return name;
caml_stat_free(name);
/* Buffer is too small, but namelen now contains the size needed */
name = caml_stat_alloc(namelen);
if (_NSGetExecutablePath(name, &namelen) == 0) return name;
caml_stat_free(name);
return NULL;
#else
int caml_executable_name(char * name, int name_len)
{
return -1;
}
return NULL;
#endif
}

View File

@ -609,13 +609,22 @@ void caml_install_invalid_parameter_handler()
/* Recover executable name */
int caml_executable_name(char * name, int name_len)
char * caml_executable_name(void)
{
int retcode;
int ret = GetModuleFileName(NULL, name, name_len);
if (0 == ret || ret >= name_len) return -1;
return 0;
char * name;
DWORD namelen, ret;
namelen = 256;
while (1) {
name = caml_stat_alloc(namelen);
ret = GetModuleFileName(NULL, name, namelen);
if (ret == 0) { caml_stat_free(name); return NULL; }
if (ret < namelen) break;
caml_stat_free(name);
if (namelen >= 1024*1024) return NULL; /* avoid runaway and overflow */
namelen *= 2;
}
return name;
}
/* snprintf emulation */