From ed08ea636745904cdaa2392c48d5eabbd8a62930 Mon Sep 17 00:00:00 2001 From: Alexander Polyakov Date: Fri, 13 Dec 2024 00:16:11 +0300 Subject: [PATCH] [k2] implement php_uname & posix_getpid (#1184) --- builtin-functions/kphp-light/file.txt | 2 - builtin-functions/kphp-light/functions.txt | 1 + builtin-functions/kphp-light/system.txt | 5 ++ .../unsupported/unsupported-server.txt | 2 - runtime-light/k2-platform/k2-api.h | 9 ++++ runtime-light/k2-platform/k2-header.h | 42 ++++++++++------ runtime-light/state/image-state.h | 49 +++++++++++++++++-- .../stdlib/system/system-functions.h | 41 +++++++++++++--- 8 files changed, 123 insertions(+), 28 deletions(-) create mode 100644 builtin-functions/kphp-light/system.txt diff --git a/builtin-functions/kphp-light/file.txt b/builtin-functions/kphp-light/file.txt index de1c546a1c..20b28f0970 100644 --- a/builtin-functions/kphp-light/file.txt +++ b/builtin-functions/kphp-light/file.txt @@ -38,8 +38,6 @@ function is_readable ($name ::: string) ::: bool; /** @kphp-extern-func-info generate-stub */ function mkdir ($name ::: string, $mode ::: int = 0777, $recursive ::: bool = false) ::: bool; /** @kphp-extern-func-info generate-stub */ -function php_uname ($mode ::: string = "a") ::: string; -/** @kphp-extern-func-info generate-stub */ function realpath ($path ::: string) ::: string | false; /** @kphp-extern-func-info generate-stub */ function rename ($oldname ::: string, $newname ::: string) ::: bool; diff --git a/builtin-functions/kphp-light/functions.txt b/builtin-functions/kphp-light/functions.txt index 3a3f4f94e3..2b34ae00d7 100644 --- a/builtin-functions/kphp-light/functions.txt +++ b/builtin-functions/kphp-light/functions.txt @@ -11,6 +11,7 @@ require_once __DIR__ . '/rpc.txt'; require_once __DIR__ . '/serialize.txt'; require_once __DIR__ . '/string.txt'; require_once __DIR__ . '/server.txt'; +require_once __DIR__ . '/system.txt'; require_once __DIR__ . '/kphp-toggles.txt'; require_once __DIR__ . '/kphp_internal.txt'; require_once __DIR__ . '/math.txt'; diff --git a/builtin-functions/kphp-light/system.txt b/builtin-functions/kphp-light/system.txt new file mode 100644 index 0000000000..444a31c1d3 --- /dev/null +++ b/builtin-functions/kphp-light/system.txt @@ -0,0 +1,5 @@ + #include #include +#include #include #define K2_API_HEADER_H @@ -85,6 +86,14 @@ inline void free_checked(void *ptr, size_t size, size_t align) noexcept { k2_exit(exit_code); } +inline uint32_t getpid() noexcept { + return k2_getpid(); +} + +inline int32_t uname(struct utsname *addr) noexcept { + return k2_uname(addr); +} + inline int32_t open(uint64_t *stream_d, size_t name_len, const char *name) noexcept { return k2_open(stream_d, name_len, name); } diff --git a/runtime-light/k2-platform/k2-header.h b/runtime-light/k2-platform/k2-header.h index 325dcd0ca0..1146a063ed 100644 --- a/runtime-light/k2-platform/k2-header.h +++ b/runtime-light/k2-platform/k2-header.h @@ -8,6 +8,7 @@ #endif // K2_API_HEADER_H #include +#include #ifdef __cplusplus #include @@ -149,6 +150,19 @@ void k2_free_checked(void *ptr, size_t size, size_t align); */ [[noreturn]] void k2_exit(int32_t exit_code); +/** + * 'k2_getpid' returns the process ID (PID) of the calling process. + */ +uint32_t k2_getpid(); + +/** + * Semantically equivalent to libc's 'uname' function. + * + * Possible 'errno': + * 'EFAULT' => buf is not valid + */ +int32_t k2_uname(struct utsname *buf); + /** * @return return `0` on success. libc-like `errno` otherwise * `stream_d` will be assigned new descriptor on success. @@ -326,13 +340,13 @@ int32_t k2_connect(uint64_t socket_d, const struct sockaddr_storage *addr, size_ * Perform sequentially `k2_lookup_host` -> `k2_socket` -> `k2_connect` * @return: `0` on success, `errno != 0` otherwise */ -int32_t k2_udp_connect(uint64_t *socket_d, const char *host, size_t host_len); +int32_t k2_udp_connect(uint64_t *socket_d, const char *hostport, size_t hostport_len); /** * Perform sequentially `k2_lookup_host` -> `k2_socket` -> `k2_connect` * @return: `0` on success, `errno != 0` otherwise */ -int32_t k2_tcp_connect(uint64_t *socket_d, const char *host, size_t host_len); +int32_t k2_tcp_connect(uint64_t *socket_d, const char *hostport, size_t hostport_len); struct SockAddr { uint16_t is_v6; @@ -343,28 +357,28 @@ struct SockAddr { }; /** - * Optimistically tries to parse `host` as `ip` + `port`. + * Optimistically tries to parse `hostport` as `ip` + `port`. * Examples: - * IpV4 `host` format: `"123.123.123.123:8080"`. - * IpV6 `host` format: `"[2001:db8::1]:8080"`. + * IpV4 `hostport` format: `"123.123.123.123:8080"`. + * IpV6 `hostport` format: `"[2001:db8::1]:8080"`. * * Then performs a DNS resolution with `man 3 getaddrinfo` (implementation depends on the host machine settings). * Examples: - * `host = "localhost:8080"` - * `host = "vk.com:443"` + * `hostport = "localhost:8080"` + * `hostport = "vk.com:443"` * * * Although the function signature is synchronous DNS resolution may perform file reading and networking. * k2-node will attempt to do this work in the background, but the component execution and associated work will be suspended. * Thus, this is an expensive operation that should have an asynchronous signature, but it does not. * - * @param `host` should be valid utf-8. + * @param `hostport` should be valid utf-8. * @return: `0` on success, `errno != 0` otherwise * on success: * `result_buf_len` is set to the number of resolved addresses. `0 <= result_buf_len <= min(result_buf_len, 128)`. * the first `result_buf_len` items of `result_buf` will be filled with resolved addresses */ -int32_t k2_lookup_host(const char *host, size_t host_len, struct SockAddr *result_buf, size_t *result_buf_len); +int32_t k2_lookup_host(const char *hostport, size_t hostport_len, struct SockAddr *result_buf, size_t *result_buf_len); /** * Only available during `k2_create_component` call. Returns `0` in other context. @@ -374,22 +388,22 @@ int32_t k2_lookup_host(const char *host, size_t host_len, struct SockAddr *resul uint32_t k2_args_count(); /** - * @param `arg_num` must satisfy `0 <= arg_num <= k2_args_count()` + * @param `arg_num` must satisfy `0 <= arg_num < k2_args_count()` * @return length of argument key with number `arg_num` in bytes */ uint32_t k2_args_key_len(uint32_t arg_num); /** - * @param `arg_num` must satisfy `0 <= arg_num <= k2_args_count()` + * @param `arg_num` must satisfy `0 <= arg_num < k2_args_count()` * @return length of argumen value with number `arg_num` in bytes */ uint32_t k2_args_value_len(uint32_t arg_num); /** * Note: key and value are **not** null-terminated. - * @param `arg_num` must satisfy `0 <= arg_num <= k2_args_count()` - * @param `key` buffer where key will be written, buffer len must staisfy `0 <= len <= k2_args_key_len(arg_num)` - * @param `value` buffer where value will be written, buffer len must staisfy `0 <= len <= k2_args_value_len(arg_num)` + * @param `arg_num` must satisfy `0 <= arg_num < k2_args_count()` + * @param `key` buffer where key will be written, buffer len must staisfy `len >= k2_args_key_len(arg_num)` + * @param `value` buffer where value will be written, buffer len must staisfy `len >= k2_args_value_len(arg_num)` */ void k2_args_fetch(uint32_t arg_num, char *key, char *value); diff --git a/runtime-light/state/image-state.h b/runtime-light/state/image-state.h index 7a293d58b3..fb0cf38585 100644 --- a/runtime-light/state/image-state.h +++ b/runtime-light/state/image-state.h @@ -5,9 +5,15 @@ #pragma once #include +#include +#include +#include #include "common/mixin/not_copyable.h" +#include "common/php-functions.h" #include "runtime-common/core/allocator/runtime-allocator.h" +#include "runtime-common/core/runtime-core.h" +#include "runtime-common/core/utils/kphp-assert-core.h" #include "runtime-light/k2-platform/k2-api.h" #include "runtime-light/stdlib/rpc/rpc-state.h" #include "runtime-light/stdlib/string/string-state.h" @@ -16,11 +22,48 @@ struct ImageState final : private vk::not_copyable { RuntimeAllocator allocator; char *c_linear_mem{nullptr}; - RpcImageState rpc_image_state{}; - StringImageState string_image_state{}; + uint32_t pid{}; + string uname_info_s; + string uname_info_n; + string uname_info_r; + string uname_info_v; + string uname_info_m; + string uname_info_a; + + RpcImageState rpc_image_state; + StringImageState string_image_state; ImageState() noexcept - : allocator(INIT_IMAGE_ALLOCATOR_SIZE, 0) {} + : allocator(INIT_IMAGE_ALLOCATOR_SIZE, 0) + , pid(k2::getpid()) { + utsname uname_info{}; + if (const auto err{k2::uname(std::addressof(uname_info))}; err != k2::errno_ok) [[unlikely]] { + php_error("can't get uname, error '%d'", err); + } + uname_info_s = string{uname_info.sysname}; + uname_info_n = string{uname_info.nodename}; + uname_info_r = string{uname_info.release}; + uname_info_v = string{uname_info.version}; + uname_info_m = string{uname_info.machine}; + // +4 for whitespaces + uname_info_a.reserve_at_least(uname_info_s.size() + uname_info_n.size() + uname_info_r.size() + uname_info_v.size() + uname_info_m.size() + 4); + uname_info_a.append(uname_info_s); + uname_info_a.push_back(' '); + uname_info_a.append(uname_info_n); + uname_info_a.push_back(' '); + uname_info_a.append(uname_info_r); + uname_info_a.push_back(' '); + uname_info_a.append(uname_info_v); + uname_info_a.push_back(' '); + uname_info_a.append(uname_info_m); + // prevent race condition on reference counter + uname_info_s.set_reference_counter_to(ExtraRefCnt::for_global_const); + uname_info_n.set_reference_counter_to(ExtraRefCnt::for_global_const); + uname_info_r.set_reference_counter_to(ExtraRefCnt::for_global_const); + uname_info_v.set_reference_counter_to(ExtraRefCnt::for_global_const); + uname_info_m.set_reference_counter_to(ExtraRefCnt::for_global_const); + uname_info_a.set_reference_counter_to(ExtraRefCnt::for_global_const); + } static const ImageState &get() noexcept { return *k2::image_state(); diff --git a/runtime-light/stdlib/system/system-functions.h b/runtime-light/stdlib/system/system-functions.h index 1a3182ff13..562135675c 100644 --- a/runtime-light/stdlib/system/system-functions.h +++ b/runtime-light/stdlib/system/system-functions.h @@ -4,35 +4,39 @@ #pragma once +#include + +#include "runtime-common/core/runtime-core.h" #include "runtime-common/core/utils/kphp-assert-core.h" +#include "runtime-light/state/image-state.h" #include "runtime-light/stdlib/system/system-state.h" template -int64_t f$estimate_memory_usage(const T &) { +int64_t f$estimate_memory_usage(const T & /*unused*/) { php_critical_error("call to unsupported function"); } template -void f$register_kphp_on_warning_callback(F &&callback) { +void f$register_kphp_on_warning_callback(F && /*callback*/) { php_critical_error("call to unsupported function"); } template -bool f$register_kphp_on_oom_callback(F &&callback) { +bool f$register_kphp_on_oom_callback(F && /*callback*/) { php_critical_error("call to unsupported function"); } template -void f$kphp_extended_instance_cache_metrics_init(F &&callback) { +void f$kphp_extended_instance_cache_metrics_init(F && /*callback*/) { php_critical_error("call to unsupported function"); } -inline int64_t f$system(const string &command, int64_t &result_code = SystemInstanceState::get().result_code_dummy) { +inline int64_t f$system(const string & /*command*/, int64_t & /*result_code*/ = SystemInstanceState::get().result_code_dummy) { php_critical_error("call to unsupported function"); } -inline Optional> f$getopt(const string &options, array longopts = {}, - Optional &rest_index = SystemInstanceState::get().rest_index_dummy) { +inline Optional> f$getopt(const string & /*options*/, const array & /*longopts*/ = {}, + Optional & /*rest_index*/ = SystemInstanceState::get().rest_index_dummy) { php_critical_error("call to unsupported function"); } @@ -42,3 +46,26 @@ inline int64_t f$numa_get_bound_node() noexcept { inline void f$kphp_set_context_on_error([[maybe_unused]] const array &tags, [[maybe_unused]] const array &extra_info, [[maybe_unused]] const string &env = {}) noexcept {} + +inline int64_t f$posix_getpid() noexcept { + return static_cast(ImageState::get().pid); +} + +inline string f$php_uname(const string &mode = string{1, 'a'}) noexcept { + const auto &image_st{ImageState::get()}; + const char mode_c{mode.empty() ? 'a' : mode[0]}; + switch (mode_c) { + case 's': + return image_st.uname_info_s; + case 'n': + return image_st.uname_info_n; + case 'r': + return image_st.uname_info_r; + case 'v': + return image_st.uname_info_v; + case 'm': + return image_st.uname_info_m; + default: + return image_st.uname_info_a; + } +}