unix,win: introduce uv_exepath2

Fixes: https://github.com/libuv/libuv/issues/4911
Signed-off-by: Juan José Arboleda <soyjuanarbol@gmail.com>
This commit is contained in:
Juan José Arboleda 2025-10-28 16:08:37 -05:00
parent 12d1ed1380
commit b05665a380
18 changed files with 146 additions and 22 deletions

View File

@ -522,6 +522,17 @@ API
Gets the executable path. You *must* call `uv_setup_args` before calling
this function.
.. c:function:: int uv_exepath2(char* buffer, size_t* size)
Like :c:func:`uv_exepath`, but returns :c:macro:`UV_ENOBUFS` if ``buffer``
is too small and sets ``*size`` to the required length (including NUL). On
success, ``*size`` is set to the path length *excluding* the NUL byte.
Returns :c:macro:`UV_ENOTSUP` on unsupported platforms.
You *must* call :c:func:`uv_setup_args` before using this function.
.. versionadded:: 1.52.0
.. c:function:: int uv_cwd(char* buffer, size_t* size)
Gets the current working directory, and stores it in `buffer`. If the

View File

@ -1824,6 +1824,7 @@ UV_EXTERN int uv_if_indextoiid(unsigned int ifindex,
size_t* size);
UV_EXTERN int uv_exepath(char* buffer, size_t* size);
UV_EXTERN int uv_exepath2(char* buffer, size_t* size);
UV_EXTERN int uv_cwd(char* buffer, size_t* size);

View File

@ -57,7 +57,7 @@ uint64_t uv__hrtime(uv_clocktype_t type) {
* or through some libc APIs. The below approach is to parse the argv[0]'s pattern
* and use it in conjunction with PATH environment variable to craft one.
*/
int uv_exepath(char* buffer, size_t* size) {
int uv__exepath(char* buffer, size_t* size, int return_enobufs) {
int res;
char args[UV__PATH_MAX];
size_t cached_len;
@ -73,6 +73,12 @@ int uv_exepath(char* buffer, size_t* size) {
*size -= 1;
if (*size > cached_len)
*size = cached_len;
if (return_enobufs && cached_len > *size) {
uv_mutex_unlock(&process_title_mutex);
return UV_ENOBUFS;
}
memcpy(buffer, original_exepath, *size);
buffer[*size] = '\0';
uv_mutex_unlock(&process_title_mutex);

View File

@ -63,7 +63,7 @@ uint64_t uv__hrtime(uv_clocktype_t type) {
}
int uv_exepath(char* buffer, size_t* size) {
int uv__exepath(char* buffer, size_t* size, int return_enobufs) {
/* realpath(exepath) may be > PATH_MAX so double it to be on the safe side. */
char abspath[PATH_MAX * 2 + 1];
char exepath[PATH_MAX + 1];
@ -84,6 +84,10 @@ int uv_exepath(char* buffer, size_t* size) {
if (abspath_size == 0)
return UV_EIO;
/* Return ENOBUFS if resize is requested and buffer is too small. */
if (return_enobufs && *size < abspath_size)
return UV_ENOBUFS;
*size -= 1;
if (*size > abspath_size)
*size = abspath_size;

View File

@ -61,7 +61,8 @@ int uv__platform_loop_init(uv_loop_t* loop) {
void uv__platform_loop_delete(uv_loop_t* loop) {
}
int uv_exepath(char* buffer, size_t* size) {
int uv__exepath(char* buffer, size_t* size, int return_enobufs) {
char abspath[PATH_MAX * 2 + 1];
int mib[4];
size_t abspath_size;
@ -82,6 +83,9 @@ int uv_exepath(char* buffer, size_t* size) {
abspath_size -= 1;
*size -= 1;
if (return_enobufs && *size < abspath_size)
return UV_ENOBUFS;
if (*size > abspath_size)
*size = abspath_size;
@ -91,6 +95,7 @@ int uv_exepath(char* buffer, size_t* size) {
return 0;
}
uint64_t uv_get_free_memory(void) {
int freecount;
size_t size = sizeof(freecount);

View File

@ -33,7 +33,7 @@ void uv_loadavg(double avg[3]) {
}
int uv_exepath(char* buffer, size_t* size) {
int uv__exepath(char* buffer, size_t* size, int return_enobufs) {
char abspath[B_PATH_NAME_LENGTH];
status_t status;
ssize_t abspath_len;
@ -46,10 +46,16 @@ int uv_exepath(char* buffer, size_t* size) {
if (status != B_OK)
return UV__ERR(status);
abspath_len = uv__strscpy(buffer, abspath, *size);
abspath_len = strlen(abspath);
*size -= 1;
if (abspath_len >= 0 && *size > (size_t)abspath_len)
*size = (size_t)abspath_len;
if (return_enobufs && *size < (size_t) abspath_len) {
*size = (size_t)abspath_len + 1;
return UV_ENOBUFS;
}
abspath_len = uv__strscpy(buffer, abspath, *size);
if (abspath_len >= 0 && *size > (size_t) abspath_len)
*size = (size_t) abspath_len;
return 0;
}

View File

@ -36,7 +36,7 @@
#include <string.h>
#include <limits.h>
int uv_exepath(char* buffer, size_t* size) {
int uv__exepath(char* buffer, size_t* size, int return_enobufs) {
kern_return_t err;
/* XXX in current Hurd, strings are char arrays of 1024 elements */
string_t exepath;
@ -45,6 +45,9 @@ int uv_exepath(char* buffer, size_t* size) {
if (buffer == NULL || size == NULL || *size == 0)
return UV_EINVAL;
if (return_enobufs)
return UV_ENOTSUP;
if (*size - 1 > 0) {
/* XXX limited length of buffer in current Hurd, this API will probably
* evolve in the future */

View File

@ -63,13 +63,14 @@ void uv_loadavg(double avg[3]) {
}
int uv_exepath(char* buffer, size_t* size) {
int uv__exepath(char* buffer, size_t* size, int return_enobufs) {
/* Intermediate buffer, retrieving partial path name does not work
* As of NetBSD-8(beta), vnode->path translator does not handle files
* with longer names than 31 characters.
*/
char int_buf[PATH_MAX];
size_t int_size;
size_t required;
int mib[4];
if (buffer == NULL || size == NULL || *size == 0)
@ -84,6 +85,14 @@ int uv_exepath(char* buffer, size_t* size) {
if (sysctl(mib, 4, int_buf, &int_size, NULL, 0))
return UV__ERR(errno);
/* Determine required size. */
required = strlen(int_buf) + 1;
if (return_enobufs && *size < required) {
/* Set new size. */
*size = required;
return UV_ENOBUFS;
}
/* Copy string from the intermediate buffer to outer one with appropriate
* length.
*/
@ -91,7 +100,7 @@ int uv_exepath(char* buffer, size_t* size) {
uv__strscpy(buffer, int_buf, *size);
/* Set new size. */
*size = strlen(buffer);
*size = required;
return 0;
}

View File

@ -58,7 +58,7 @@ void uv_loadavg(double avg[3]) {
}
int uv_exepath(char* buffer, size_t* size) {
int uv__exepath(char* buffer, size_t* size, int return_enobufs) {
int mib[4];
char **argsbuf = NULL;
size_t argsbuf_size = 100U;
@ -96,6 +96,11 @@ int uv_exepath(char* buffer, size_t* size) {
*size -= 1;
exepath_size = strlen(argsbuf[0]);
if (return_enobufs && *size < exepath_size) {
*size = exepath_size;
return UV_ENOBUFS;
}
if (*size > exepath_size)
*size = exepath_size;

View File

@ -152,14 +152,16 @@ static int getexe(char* buf, size_t len) {
* or through some libc APIs. The below approach is to parse the argv[0]'s pattern
* and use it in conjunction with PATH environment variable to craft one.
*/
int uv_exepath(char* buffer, size_t* size) {
int uv__exepath(char* buffer, size_t* size, int return_enobufs) {
int res;
char args[PATH_MAX];
int pid;
if (buffer == NULL || size == NULL || *size == 0)
return UV_EINVAL;
if (return_enobufs)
return UV_ENOTSUP;
res = getexe(args, sizeof(args));
if (res < 0)
return UV_EINVAL;

View File

@ -25,21 +25,32 @@
#include <stddef.h>
#include <unistd.h>
int uv_exepath(char* buffer, size_t* size) {
int uv__exepath(char* buffer, size_t* size, int return_enobufs) {
char tmp[4096];
ssize_t n;
if (buffer == NULL || size == NULL || *size == 0)
return UV_EINVAL;
n = *size - 1;
if (n > 0)
n = readlink("/proc/self/exe", buffer, n);
n = readlink("/proc/self/exe", tmp, sizeof(tmp) - 1);
if (n == -1)
return UV__ERR(errno);
tmp[n] = '\0';
if (return_enobufs && *size < (size_t) n) {
*size = (size_t) n + 1;
return UV_ENOBUFS;
}
n = uv__strscpy(buffer, tmp, *size);
if (n < 0) /* Trucated but still ok */
n = *size - 1;
buffer[n] = '\0';
*size = n;
*size = (size_t) n;
return 0;
}

View File

@ -63,11 +63,14 @@ void uv_loadavg(double avg[3]) {
}
int uv_exepath(char* buffer, size_t* size) {
int uv__exepath(char* buffer, size_t* size, int return_enobufs) {
char path[PATH_MAX];
if (buffer == NULL || size == NULL || *size == 0)
return UV_EINVAL;
if (return_enobufs)
return UV_ENOTSUP;
realpath(_cmdname(NULL), path);
strlcpy(buffer, path, *size);
*size = strlen(buffer);

View File

@ -380,7 +380,7 @@ uint64_t uv__hrtime(uv_clocktype_t type) {
* of the function, but this function could be called by multiple consumers and
* we don't want to potentially create a race condition in the use of snprintf.
*/
int uv_exepath(char* buffer, size_t* size) {
int uv__exepath(char* buffer, size_t* size, int return_enobufs) {
ssize_t res;
char buf[128];
@ -396,6 +396,11 @@ int uv_exepath(char* buffer, size_t* size) {
if (res == -1)
return UV__ERR(errno);
if (return_enobufs && *size < (size_t) res) {
*size = (size_t) res;
return UV_ENOBUFS;
}
buffer[res] = '\0';
*size = res;
return 0;

View File

@ -1055,6 +1055,17 @@ uint64_t uv_metrics_idle_time(uv_loop_t* loop) {
return idle_time;
}
int uv_exepath(char *buffer, size_t *size) {
return uv__exepath(buffer, size, /*return_enobufs*/0);
}
int uv_exepath2(char *buffer, size_t *size) {
return uv__exepath(buffer, size, /*return_enobufs*/1);
}
/* OS390 needs a different implementation, already provided in os390.c. */
#ifndef __MVS__
void uv_free_interface_addresses(uv_interface_address_t* addresses,

View File

@ -211,6 +211,12 @@ void uv__fs_poll_close(uv_fs_poll_t* handle);
int uv__getaddrinfo_translate_error(int sys_err); /* EAI_* error. */
int uv__exepath(char* buffer, size_t* size, int return_enobufs);
int uv_exepath(char *buffer, size_t *size);
int uv_exepath2(char *buffer, size_t *size);
enum uv__work_kind {
UV__WORK_CPU,
UV__WORK_FAST_IO,

View File

@ -94,9 +94,10 @@ void uv__util_init(void) {
}
int uv_exepath(char* buffer, size_t* size_ptr) {
int uv__exepath(char* buffer, size_t* size_ptr, int return_enobufs) {
size_t utf8_len, utf16_buffer_len, utf16_len;
WCHAR* utf16_buffer;
int required_utf8_len;
int err;
if (buffer == NULL || size_ptr == NULL || *size_ptr == 0) {
@ -122,6 +123,23 @@ int uv_exepath(char* buffer, size_t* size_ptr) {
goto error;
}
/* Determine how many UTF-8 bytes are needed. */
required_utf8_len = WideCharToMultiByte(CP_UTF8, 0,
utf16_buffer, (int)utf16_len,
NULL, 0, NULL, NULL);
/* Just return error if return enobufs is requested */
if (return_enobufs && required_utf8_len <= 0) {
err = GetLastError();
uv__free(utf16_buffer);
return uv_translate_sys_error(err);
}
if (return_enobufs && *size_ptr < (size_t) required_utf8_len + 1) {
*size_ptr = (size_t) required_utf8_len + 1;
uv__free(utf16_buffer);
return UV_ENOBUFS;
}
/* Convert to UTF-8 */
utf8_len = *size_ptr - 1; /* Reserve space for NUL */
err = uv_utf16_to_wtf8(utf16_buffer, utf16_len, &buffer, &utf8_len);

View File

@ -289,6 +289,7 @@ TEST_DECLARE (eintr_handling)
TEST_DECLARE (get_currentexe)
TEST_DECLARE (process_title)
TEST_DECLARE (process_title_big_argv)
TEST_DECLARE (exepath2_enobufs)
TEST_DECLARE (process_title_threadsafe)
TEST_DECLARE (cwd_and_chdir)
TEST_DECLARE (get_memory)
@ -942,6 +943,7 @@ TASK_LIST_START
TEST_ENTRY (process_title)
TEST_ENTRY (process_title_big_argv)
TEST_ENTRY (exepath2_enobufs)
TEST_ENTRY (process_title_threadsafe)
TEST_ENTRY (cwd_and_chdir)

View File

@ -83,6 +83,22 @@ static void exit_cb(uv_process_t* process, int64_t status, int signo) {
}
TEST_IMPL(exepath2_enobufs) {
int r;
char smallbuf[2];
size_t small_size;
/* Test uv_exepath2
* Refs: https://github.com/libuv/libuv/issues/4911 */
small_size = sizeof(smallbuf);
r = uv_exepath2(smallbuf, &small_size);
ASSERT(r == UV_ENOBUFS || r == UV_ENOTSUP);
ASSERT_GE(small_size, sizeof(smallbuf));
return 0;
}
TEST_IMPL(process_title_big_argv) {
uv_process_options_t options;
uv_process_t process;