diff --git a/lib/system/system.toit b/lib/system/system.toit index f1bff1dd1..2dbbffc51 100644 --- a/lib/system/system.toit +++ b/lib/system/system.toit @@ -3,6 +3,12 @@ // found in the lib/LICENSE file. import system.trace show send-trace-message +import system.storage + +// Use lazy initialization to delay opening the storage bucket +// until we need it the first time. From that point forward, +// we keep it around forever. +bucket_/storage.Bucket ::= storage.Bucket.open --flash "toitlang.org/system" /** The number of bits per byte. */ BITS-PER-BYTE ::= 8 @@ -247,3 +253,28 @@ If the program is run as an executable, this is the fully resolved path to the */ program-path -> string?: #primitive.core.program-path + +/** +The hostname of the machine running the program. +*/ +hostname -> string: + if platform == PLATFORM-FREERTOS: + config-name := bucket_.get "hostname" + if config-name: return config-name + return hostname_ + +/** +Sets the hostname of the machine running the program. + +This operation is not supported on all platforms. + +Only new network connections will use the new hostname. Also, some + routers may cache the old hostname for a while. +*/ +hostname= hostname/string -> none: + if platform != PLATFORM-FREERTOS: + throw "Setting hostname is not supported on this platform" + bucket_["hostname"] = hostname + +hostname_ -> string: + #primitive.core.hostname diff --git a/src/compiler/propagation/type_primitive_core.cc b/src/compiler/propagation/type_primitive_core.cc index 6f403776d..5f23bb849 100644 --- a/src/compiler/propagation/type_primitive_core.cc +++ b/src/compiler/propagation/type_primitive_core.cc @@ -290,6 +290,8 @@ TYPE_PRIMITIVE_ANY(firmware_mapping_at) TYPE_PRIMITIVE_ANY(firmware_mapping_copy) TYPE_PRIMITIVE_BYTE_ARRAY(rtc_user_bytes) +TYPE_PRIMITIVE_STRING(hostname) + bool TypePrimitive::uses_entry_task(unsigned module, unsigned index) { return module == INDEX_core && index == CoreIndexes::task_new; } diff --git a/src/compiler/propagation/type_primitive_ethernet.cc b/src/compiler/propagation/type_primitive_ethernet.cc index 07e1653b4..e67b93183 100644 --- a/src/compiler/propagation/type_primitive_ethernet.cc +++ b/src/compiler/propagation/type_primitive_ethernet.cc @@ -27,6 +27,7 @@ TYPE_PRIMITIVE_ANY(connect) TYPE_PRIMITIVE_ANY(setup_ip) TYPE_PRIMITIVE_ANY(disconnect) TYPE_PRIMITIVE_ANY(get_ip) +TYPE_PRIMITIVE_ANY(set_hostname) } // namespace toit::compiler } // namespace toit diff --git a/src/compiler/propagation/type_primitive_wifi.cc b/src/compiler/propagation/type_primitive_wifi.cc index e70f99b70..6393b91ce 100644 --- a/src/compiler/propagation/type_primitive_wifi.cc +++ b/src/compiler/propagation/type_primitive_wifi.cc @@ -31,6 +31,7 @@ TYPE_PRIMITIVE_ANY(get_ip) TYPE_PRIMITIVE_ANY(init_scan) TYPE_PRIMITIVE_ANY(start_scan) TYPE_PRIMITIVE_ANY(read_scan) +TYPE_PRIMITIVE_ANY(set_hostname) TYPE_PRIMITIVE_ARRAY(ap_info) } // namespace toit::compiler diff --git a/src/primitive.h b/src/primitive.h index d850c4de0..d9c947d92 100644 --- a/src/primitive.h +++ b/src/primitive.h @@ -256,6 +256,7 @@ namespace toit { PRIMITIVE(firmware_mapping_at, 2) \ PRIMITIVE(firmware_mapping_copy, 5) \ PRIMITIVE(rtc_user_bytes, 0) \ + PRIMITIVE(hostname, 0) \ #define MODULE_TIMER(PRIMITIVE) \ PRIMITIVE(init, 0) \ @@ -324,6 +325,7 @@ namespace toit { PRIMITIVE(init_scan, 1) \ PRIMITIVE(start_scan, 4) \ PRIMITIVE(read_scan, 1) \ + PRIMITIVE(set_hostname, 2) \ PRIMITIVE(ap_info, 1) \ #define MODULE_ETHERNET(PRIMITIVE) \ @@ -334,6 +336,7 @@ namespace toit { PRIMITIVE(setup_ip, 1) \ PRIMITIVE(disconnect, 2) \ PRIMITIVE(get_ip, 1) \ + PRIMITIVE(set_hostname, 2) \ #define MODULE_BLE(PRIMITIVE) \ PRIMITIVE(init, 0) \ diff --git a/src/primitive_core.cc b/src/primitive_core.cc index 4da2df666..010e0f08b 100644 --- a/src/primitive_core.cc +++ b/src/primitive_core.cc @@ -2571,4 +2571,19 @@ PRIMITIVE(rtc_user_bytes) { } #endif +PRIMITIVE(hostname) { + const char* name; +#ifdef TOIT_ESP32 + name = CONFIG_LWIP_LOCAL_HOSTNAME; +#else + char buffer[HOST_NAME_MAX + 1]; + int result = gethostname(buffer, sizeof(buffer)); + if (result != 0) { + return Primitive::os_error(errno, process); + } + name = buffer; +#endif + return process->allocate_string_or_error(name); +} + } // namespace toit diff --git a/src/resources/ethernet_esp32.cc b/src/resources/ethernet_esp32.cc index cdc30e320..e4db25995 100644 --- a/src/resources/ethernet_esp32.cc +++ b/src/resources/ethernet_esp32.cc @@ -91,6 +91,10 @@ class EthernetResourceGroup : public ResourceGroup { uint32_t on_event(Resource* resource, word data, uint32_t state); + esp_err_t set_hostname(const char* hostname) { + return esp_netif_set_hostname(netif_, hostname); + } + private: int id_; esp_eth_mac_t* mac_; @@ -449,6 +453,16 @@ PRIMITIVE(get_ip) { return result; } +PRIMITIVE(set_hostname) { + ARGS(EthernetResourceGroup, group, cstring, hostname); + + if (strlen(hostname) > 32) FAIL(INVALID_ARGUMENT); + + esp_err_t err = group->set_hostname(hostname); + if (err != ESP_OK) return Primitive::os_error(err, process); + + return process->null_object(); +} } // namespace toit diff --git a/src/resources/wifi_esp32.cc b/src/resources/wifi_esp32.cc index 6973c92d6..3a08eda56 100644 --- a/src/resources/wifi_esp32.cc +++ b/src/resources/wifi_esp32.cc @@ -82,6 +82,10 @@ class WifiResourceGroup : public ResourceGroup { esp_err_t start_scan(bool passive, int channel, uint32_t period_ms); + esp_err_t set_hostname(const char* hostname) { + return esp_netif_set_hostname(netif_, hostname); + } + ~WifiResourceGroup() { esp_err_t err = ESP_OK; for (int i = 0; i < DEINIT_ATTEMPTS; i++) { @@ -464,7 +468,6 @@ esp_err_t WifiResourceGroup::start_scan(bool passive, int channel, uint32_t peri return esp_wifi_scan_start(&config, false); } - MODULE_IMPLEMENTATION(wifi, MODULE_WIFI) PRIMITIVE(init) { @@ -763,6 +766,17 @@ PRIMITIVE(read_scan) { return ap_array; } +PRIMITIVE(set_hostname) { + ARGS(WifiResourceGroup, group, cstring, hostname); + + if (strlen(hostname) > 32) FAIL(INVALID_ARGUMENT); + + esp_err_t err = group->set_hostname(hostname); + if (err != ESP_OK) return Primitive::os_error(err, process); + + return process->null_object(); +} + PRIMITIVE(ap_info) { ARGS(WifiResourceGroup, group); diff --git a/system/extensions/esp32/wifi.toit b/system/extensions/esp32/wifi.toit index 0f5b6ba10..1e2464a35 100644 --- a/system/extensions/esp32/wifi.toit +++ b/system/extensions/esp32/wifi.toit @@ -23,6 +23,7 @@ import net.udp import net.wifi import encoding.tison +import system import system.assets import system.firmware import system.storage @@ -226,6 +227,7 @@ class WifiModule implements NetworkModule: return address_ connect -> none: + wifi-set-hostname_ resource-group_ system.hostname with-timeout WIFI-CONNECT-TIMEOUT_: wait-for-connected_ if ap: wait-for-static-ip-address_ @@ -357,6 +359,9 @@ class WifiModule implements NetworkModule: wifi-init_ ap: #primitive.wifi.init +wifi-set-hostname_ resource-group hostname: + #primitive.wifi.set-hostname + wifi-close_ resource-group: #primitive.wifi.close diff --git a/tests/hostname-test.toit b/tests/hostname-test.toit new file mode 100644 index 000000000..b6da367ec --- /dev/null +++ b/tests/hostname-test.toit @@ -0,0 +1,19 @@ +// Copyright (C) 2024 Toitware ApS. +// Use of this source code is governed by a Zero-Clause BSD license that can +// be found in the tests/LICENSE file. + +import expect show * +import host.pipe +import system + +main: + expected-hostname/string := ? + if system.platform == system.PLATFORM-WINDOWS: + expected-hostname = pipe.backticks "hostname" + else if system.platform == system.PLATFORM-LINUX: + expected-hostname = pipe.backticks "hostnamectl" "hostname" + else: + expected-hostname = pipe.backticks "hostname" "-s" + expected-hostname = expected-hostname.trim + + expect-equals expected-hostname system.hostname