diff --git a/docs/src/misc.rst b/docs/src/misc.rst index db95e2dde..88c7a2c1c 100644 --- a/docs/src/misc.rst +++ b/docs/src/misc.rst @@ -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 diff --git a/include/uv.h b/include/uv.h index d1a77c749..3b539fee5 100644 --- a/include/uv.h +++ b/include/uv.h @@ -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); diff --git a/src/unix/aix-common.c b/src/unix/aix-common.c index abc4c901a..423e9c407 100644 --- a/src/unix/aix-common.c +++ b/src/unix/aix-common.c @@ -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); diff --git a/src/unix/darwin.c b/src/unix/darwin.c index 98ddf2728..408a31ffc 100644 --- a/src/unix/darwin.c +++ b/src/unix/darwin.c @@ -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; diff --git a/src/unix/freebsd.c b/src/unix/freebsd.c index a6de29c55..e20011197 100644 --- a/src/unix/freebsd.c +++ b/src/unix/freebsd.c @@ -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); diff --git a/src/unix/haiku.c b/src/unix/haiku.c index 0d3645f01..964b1f3f1 100644 --- a/src/unix/haiku.c +++ b/src/unix/haiku.c @@ -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; } diff --git a/src/unix/hurd.c b/src/unix/hurd.c index 63c878123..7c204ccf0 100644 --- a/src/unix/hurd.c +++ b/src/unix/hurd.c @@ -36,7 +36,7 @@ #include #include -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 */ diff --git a/src/unix/netbsd.c b/src/unix/netbsd.c index fa21e98e4..3a9385cdf 100644 --- a/src/unix/netbsd.c +++ b/src/unix/netbsd.c @@ -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; } diff --git a/src/unix/openbsd.c b/src/unix/openbsd.c index cf20fa665..54c86ee0b 100644 --- a/src/unix/openbsd.c +++ b/src/unix/openbsd.c @@ -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; diff --git a/src/unix/os390.c b/src/unix/os390.c index 029add414..11ad8ab38 100644 --- a/src/unix/os390.c +++ b/src/unix/os390.c @@ -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; diff --git a/src/unix/procfs-exepath.c b/src/unix/procfs-exepath.c index 00dc021f2..ca4189a5f 100644 --- a/src/unix/procfs-exepath.c +++ b/src/unix/procfs-exepath.c @@ -25,21 +25,32 @@ #include #include -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; } diff --git a/src/unix/qnx.c b/src/unix/qnx.c index d873165fe..94b162b7c 100644 --- a/src/unix/qnx.c +++ b/src/unix/qnx.c @@ -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); diff --git a/src/unix/sunos.c b/src/unix/sunos.c index 220ff74bd..7123baabc 100644 --- a/src/unix/sunos.c +++ b/src/unix/sunos.c @@ -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; diff --git a/src/uv-common.c b/src/uv-common.c index e5a763290..d85101073 100644 --- a/src/uv-common.c +++ b/src/uv-common.c @@ -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, diff --git a/src/uv-common.h b/src/uv-common.h index b9a8e976e..7ef11b4ad 100644 --- a/src/uv-common.h +++ b/src/uv-common.h @@ -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, diff --git a/src/win/util.c b/src/win/util.c index 9fb5694c9..0d29f783b 100644 --- a/src/win/util.c +++ b/src/win/util.c @@ -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); diff --git a/test/test-list.h b/test/test-list.h index 954eff330..e9f485edd 100644 --- a/test/test-list.h +++ b/test/test-list.h @@ -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) diff --git a/test/test-process-title.c b/test/test-process-title.c index 3478033a9..6b1488cb0 100644 --- a/test/test-process-title.c +++ b/test/test-process-title.c @@ -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;