// SPDX-License-Identifier: Apache-2.0 #include #include #include #include #include #include #define TAG "service_registration" // Defined in service_instance.cpp. Internal-only: lets the registry drive state // transitions without exposing mutation on the public ServiceInstance API. extern "C" void service_instance_set_state(ServiceInstance* instance, ServiceState state); struct ManifestLedger { std::unordered_map manifests; Mutex mutex {}; ManifestLedger() { mutex_construct(&mutex); } ~ManifestLedger() { mutex_destruct(&mutex); } }; struct InstanceLedger { std::unordered_map instances; Mutex mutex {}; InstanceLedger() { mutex_construct(&mutex); } ~InstanceLedger() { mutex_destruct(&mutex); } }; static ManifestLedger& get_manifest_ledger() { static ManifestLedger ledger; return ledger; } static InstanceLedger& get_instance_ledger() { static InstanceLedger ledger; return ledger; } #define manifest_ledger get_manifest_ledger() #define instance_ledger get_instance_ledger() extern "C" { error_t service_manager_add(const ServiceManifest* manifest, bool auto_start) { mutex_lock(&manifest_ledger.mutex); if (manifest_ledger.manifests.contains(manifest->id)) { mutex_unlock(&manifest_ledger.mutex); LOG_E(TAG, "Manifest with id '%s' is already registered", manifest->id); return ERROR_INVALID_ARGUMENT; } manifest_ledger.manifests[manifest->id] = manifest; mutex_unlock(&manifest_ledger.mutex); LOG_I(TAG, "add %s", manifest->id); if (auto_start) { return service_manager_start(manifest->id); } return ERROR_NONE; } error_t service_manager_remove(const char* id) { if (service_manager_get_state(id) != SERVICE_STATE_STOPPED) { return ERROR_INVALID_STATE; } mutex_lock(&manifest_ledger.mutex); const auto iterator = manifest_ledger.manifests.find(id); if (iterator == manifest_ledger.manifests.end()) { mutex_unlock(&manifest_ledger.mutex); return ERROR_NOT_FOUND; } manifest_ledger.manifests.erase(iterator); mutex_unlock(&manifest_ledger.mutex); LOG_I(TAG, "remove %s", id); return ERROR_NONE; } error_t service_manager_start(const char* id) { mutex_lock(&manifest_ledger.mutex); const auto manifest_iterator = manifest_ledger.manifests.find(id); if (manifest_iterator == manifest_ledger.manifests.end()) { mutex_unlock(&manifest_ledger.mutex); return ERROR_NOT_FOUND; } const ServiceManifest* manifest = manifest_iterator->second; mutex_unlock(&manifest_ledger.mutex); mutex_lock(&instance_ledger.mutex); if (instance_ledger.instances.contains(id)) { mutex_unlock(&instance_ledger.mutex); return ERROR_INVALID_STATE; } auto* instance = new(std::nothrow) ServiceInstance { .manifest = nullptr, .data = nullptr, .internal = nullptr }; if (instance == nullptr) { mutex_unlock(&instance_ledger.mutex); return ERROR_OUT_OF_MEMORY; } error_t error = service_instance_construct(instance, manifest); if (error != ERROR_NONE) { mutex_unlock(&instance_ledger.mutex); delete instance; return error; } // Register before on_start() so a service can find itself while starting. instance_ledger.instances[id] = instance; mutex_unlock(&instance_ledger.mutex); service_instance_set_state(instance, SERVICE_STATE_STARTING); LOG_I(TAG, "start %s", id); error = (manifest->on_start != nullptr) ? manifest->on_start(instance, instance->data) : ERROR_NONE; if (error == ERROR_NONE) { service_instance_set_state(instance, SERVICE_STATE_STARTED); return ERROR_NONE; } LOG_E(TAG, "Failed to start %s: %s", id, error_to_string(error)); service_instance_set_state(instance, SERVICE_STATE_STOPPED); mutex_lock(&instance_ledger.mutex); instance_ledger.instances.erase(id); mutex_unlock(&instance_ledger.mutex); service_instance_destruct(instance); delete instance; return ERROR_RESOURCE; } error_t service_manager_stop(const char* id) { mutex_lock(&instance_ledger.mutex); const auto iterator = instance_ledger.instances.find(id); if (iterator == instance_ledger.instances.end()) { mutex_unlock(&instance_ledger.mutex); return ERROR_NOT_FOUND; } ServiceInstance* instance = iterator->second; mutex_unlock(&instance_ledger.mutex); LOG_I(TAG, "stop %s", id); service_instance_set_state(instance, SERVICE_STATE_STOPPING); if (instance->manifest->on_stop != nullptr) { instance->manifest->on_stop(instance, instance->data); } service_instance_set_state(instance, SERVICE_STATE_STOPPED); mutex_lock(&instance_ledger.mutex); instance_ledger.instances.erase(id); mutex_unlock(&instance_ledger.mutex); service_instance_destruct(instance); delete instance; return ERROR_NONE; } ServiceState service_manager_get_state(const char* id) { mutex_lock(&instance_ledger.mutex); const auto iterator = instance_ledger.instances.find(id); if (iterator == instance_ledger.instances.end()) { mutex_unlock(&instance_ledger.mutex); return SERVICE_STATE_STOPPED; } ServiceInstance* instance = iterator->second; mutex_unlock(&instance_ledger.mutex); return service_instance_get_state(instance); } const ServiceManifest* service_manager_find_manifest(const char* id) { mutex_lock(&manifest_ledger.mutex); const auto iterator = manifest_ledger.manifests.find(id); const ServiceManifest* manifest = (iterator != manifest_ledger.manifests.end()) ? iterator->second : nullptr; mutex_unlock(&manifest_ledger.mutex); return manifest; } ServiceInstance* service_manager_find_instance(const char* id) { mutex_lock(&instance_ledger.mutex); const auto iterator = instance_ledger.instances.find(id); auto* instance = (iterator != instance_ledger.instances.end()) ? iterator->second : nullptr; mutex_unlock(&instance_ledger.mutex); return instance; } } // extern "C"