committed by
GitHub
parent
cca7224252
commit
f4f91f81d6
@@ -0,0 +1,24 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stddef.h>
|
||||
#include <tactility/error.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Get the root path for user data. Survives OS upgrades.
|
||||
* @param[out] out_path buffer to store the path (no trailing "/")
|
||||
* @param[in] out_path_size size of the output buffer
|
||||
* @retval ERROR_NOT_FOUND if the configured storage location isn't available (e.g. no SD card)
|
||||
* @retval ERROR_BUFFER_OVERFLOW if out_path_size is too small
|
||||
* @retval ERROR_NONE on success
|
||||
*/
|
||||
error_t paths_get_user_data_path(char* out_path, size_t out_path_size);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,78 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <tactility/error.h>
|
||||
#include <tactility/service/service_manifest.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct ServiceInstanceInternal;
|
||||
|
||||
/** The lifecycle state of a ServiceInstance. */
|
||||
typedef enum {
|
||||
SERVICE_STATE_STARTING,
|
||||
SERVICE_STATE_STARTED,
|
||||
SERVICE_STATE_STOPPING,
|
||||
SERVICE_STATE_STOPPED,
|
||||
} ServiceState;
|
||||
|
||||
/** Represents a running (or about-to-run) instance of a service. */
|
||||
struct ServiceInstance {
|
||||
/** The manifest that spawned this instance. */
|
||||
const struct ServiceManifest* manifest;
|
||||
/** Service-specific data, owned by the service implementation. Can be NULL. */
|
||||
void* data;
|
||||
/**
|
||||
* Internal state managed by the kernel.
|
||||
* ServiceInstance implementers should initialize this to NULL.
|
||||
* Do not access or modify directly; use service_instance_* functions.
|
||||
*/
|
||||
struct ServiceInstanceInternal* internal;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Construct a service instance.
|
||||
* @details This calls manifest->create_service() to build the instance's data.
|
||||
* @param[in,out] instance a service instance with manifest set and internal set to NULL
|
||||
* @param[in] manifest non-null manifest to construct the instance from
|
||||
* @retval ERROR_OUT_OF_MEMORY if internal data allocation failed
|
||||
* @retval ERROR_NONE on success
|
||||
*/
|
||||
error_t service_instance_construct(struct ServiceInstance* instance, const struct ServiceManifest* manifest);
|
||||
|
||||
/**
|
||||
* @brief Destruct a service instance.
|
||||
* @details This calls manifest->destroy_service() on the instance's data.
|
||||
* @param[in,out] instance non-null service instance pointer
|
||||
* @retval ERROR_INVALID_STATE if the instance is not stopped
|
||||
* @retval ERROR_NONE on success
|
||||
*/
|
||||
error_t service_instance_destruct(struct ServiceInstance* instance);
|
||||
|
||||
/**
|
||||
* @brief Get the manifest of a service instance.
|
||||
* @param[in] instance non-null service instance pointer
|
||||
* @return the manifest
|
||||
*/
|
||||
const struct ServiceManifest* service_instance_get_manifest(struct ServiceInstance* instance);
|
||||
|
||||
/**
|
||||
* @brief Get the data of a service instance.
|
||||
* @param[in] instance non-null service instance pointer
|
||||
* @return the data (can be NULL)
|
||||
*/
|
||||
void* service_instance_get_data(struct ServiceInstance* instance);
|
||||
|
||||
/**
|
||||
* @brief Get the state of a service instance.
|
||||
* @param[in] instance non-null service instance pointer
|
||||
* @return the current state
|
||||
*/
|
||||
ServiceState service_instance_get_state(struct ServiceInstance* instance);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,74 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <tactility/error.h>
|
||||
#include <tactility/service/service_instance.h>
|
||||
#include <tactility/service/service_manifest.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Register a service manifest.
|
||||
* @param[in] manifest non-null manifest to register
|
||||
* @param[in] auto_start if true, the service is started immediately after registration
|
||||
* @retval ERROR_INVALID_ARGUMENT if a manifest with the same id is already registered
|
||||
* @retval ERROR_RESOURCE if auto_start is true and starting the service failed
|
||||
* @retval ERROR_NONE on success
|
||||
*/
|
||||
error_t service_manager_add(const struct ServiceManifest* manifest, bool auto_start);
|
||||
|
||||
/**
|
||||
* @brief Unregister a previously-added manifest.
|
||||
* @param[in] id non-null service id
|
||||
* @retval ERROR_INVALID_STATE if the service is still running
|
||||
* @retval ERROR_NOT_FOUND if no manifest with this id is registered
|
||||
* @retval ERROR_NONE on success
|
||||
*/
|
||||
error_t service_manager_remove(const char* id);
|
||||
|
||||
/**
|
||||
* @brief Find a registered manifest by id.
|
||||
* @param[in] id non-null service id
|
||||
* @return the manifest, or NULL if not found
|
||||
*/
|
||||
const struct ServiceManifest* service_manager_find_manifest(const char* id);
|
||||
|
||||
/**
|
||||
* @brief Start a registered service by id.
|
||||
* @param[in] id non-null service id
|
||||
* @retval ERROR_NOT_FOUND if no manifest with this id is registered
|
||||
* @retval ERROR_INVALID_STATE if the service is already running
|
||||
* @retval ERROR_RESOURCE if the service's on_start callback failed
|
||||
* @retval ERROR_NONE on success
|
||||
*/
|
||||
error_t service_manager_start(const char* id);
|
||||
|
||||
/**
|
||||
* @brief Stop a running service by id.
|
||||
* @param[in] id non-null service id
|
||||
* @retval ERROR_NOT_FOUND if no service with this id is running
|
||||
* @retval ERROR_NONE on success
|
||||
*/
|
||||
error_t service_manager_stop(const char* id);
|
||||
|
||||
/**
|
||||
* @brief Get the state of a service by id.
|
||||
* @param[in] id non-null service id
|
||||
* @return the current state, or SERVICE_STATE_STOPPED if the id is unknown
|
||||
*/
|
||||
ServiceState service_manager_get_state(const char* id);
|
||||
|
||||
/**
|
||||
* @brief Find the service instance
|
||||
* @param[in] id non-null service id
|
||||
* @return the instance when found, otherwise return NULL
|
||||
*/
|
||||
struct ServiceInstance* service_manager_find_instance(const char* id);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,66 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <tactility/error.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// ServiceContext (declared in service_context.h) is a typedef alias of ServiceInstance,
|
||||
// so the callback types below are declared directly in terms of ServiceInstance to avoid
|
||||
// a conflicting forward-declaration of an unrelated "ServiceContext" struct tag.
|
||||
struct ServiceInstance;
|
||||
|
||||
/**
|
||||
* Allocates and initializes the service's custom data.
|
||||
* @param[in] manifest the manifest that owns this callback
|
||||
* @return the service's custom data, or NULL if it has none
|
||||
*/
|
||||
typedef void* (*ServiceCreate)(const struct ServiceManifest* manifest);
|
||||
|
||||
/**
|
||||
* Frees custom data that was set up by the matching ServiceCreate function.
|
||||
* @param[in] data the custom data returned by create_service
|
||||
* @param[in] manifest the manifest that owns this callback
|
||||
*/
|
||||
typedef void (*ServiceDestroy)(const struct ServiceManifest* manifest, void* data);
|
||||
|
||||
/**
|
||||
* Called when a service instance is starting.
|
||||
* Can be NULL, in which case starting always succeeds.
|
||||
* @param[in,out] instance the starting service instance (also its own ServiceContext)
|
||||
* @param[in] data the custom data returned by create_service
|
||||
* @return ERROR_NONE if the service started successfully
|
||||
*/
|
||||
typedef error_t (*ServiceOnStart)(struct ServiceInstance* instance, void* data);
|
||||
|
||||
/**
|
||||
* Called when a service instance is stopping.
|
||||
* Can be NULL, in which case stopping is a no-op.
|
||||
* @param[in,out] instance the stopping service instance (also its own ServiceContext)
|
||||
* @param[in] data the custom data returned by create_service
|
||||
*/
|
||||
typedef void (*ServiceOnStop)(struct ServiceInstance* instance, void* data);
|
||||
|
||||
/**
|
||||
* Describes a registrable service type.
|
||||
* One manifest exists per service id, shared across start/stop cycles.
|
||||
*/
|
||||
struct ServiceManifest {
|
||||
/** Unique service identifier. Should never be NULL. */
|
||||
const char* id;
|
||||
/** Allocates the service's custom data. Should never be NULL. */
|
||||
ServiceCreate create_service;
|
||||
/** Frees the service's custom data. Should never be NULL. */
|
||||
ServiceDestroy destroy_service;
|
||||
/** Called when a service instance starts. Can be NULL. */
|
||||
ServiceOnStart on_start;
|
||||
/** Called when a service instance stops. Can be NULL. */
|
||||
ServiceOnStop on_stop;
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,56 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stddef.h>
|
||||
#include <tactility/error.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Get the user data directory for a service. Survives OS upgrades. No trailing "/".
|
||||
* @param[in] service_id non-null service id
|
||||
* @param[out] out_path buffer to store the path
|
||||
* @param[in] out_path_size size of the output buffer
|
||||
* @retval ERROR_BUFFER_OVERFLOW if out_path_size is too small
|
||||
* @retval ERROR_NONE on success
|
||||
*/
|
||||
error_t service_paths_get_user_data_directory(const char* service_id, char* out_path, size_t out_path_size);
|
||||
|
||||
/**
|
||||
* @brief Get a path within the user data directory for a service.
|
||||
* @param[in] service_id non-null service id
|
||||
* @param[in] child_path path without a "/" prefix
|
||||
* @param[out] out_path buffer to store the path
|
||||
* @param[in] out_path_size size of the output buffer
|
||||
* @retval ERROR_BUFFER_OVERFLOW if out_path_size is too small
|
||||
* @retval ERROR_NONE on success
|
||||
*/
|
||||
error_t service_paths_get_user_data_path(const char* service_id, const char* child_path, char* out_path, size_t out_path_size);
|
||||
|
||||
/**
|
||||
* @brief Get the assets directory for a service. Do not store configuration data here. No trailing "/".
|
||||
* @param[in] service_id non-null service id
|
||||
* @param[out] out_path buffer to store the path
|
||||
* @param[in] out_path_size size of the output buffer
|
||||
* @retval ERROR_BUFFER_OVERFLOW if out_path_size is too small
|
||||
* @retval ERROR_NONE on success
|
||||
*/
|
||||
error_t service_paths_get_assets_directory(const char* service_id, char* out_path, size_t out_path_size);
|
||||
|
||||
/**
|
||||
* @brief Get a path within the assets directory for a service.
|
||||
* @param[in] service_id non-null service id
|
||||
* @param[in] child_path path without a "/" prefix
|
||||
* @param[out] out_path buffer to store the path
|
||||
* @param[in] out_path_size size of the output buffer
|
||||
* @retval ERROR_BUFFER_OVERFLOW if out_path_size is too small
|
||||
* @retval ERROR_NONE on success
|
||||
*/
|
||||
error_t service_paths_get_assets_path(const char* service_id, const char* child_path, char* out_path, size_t out_path_size);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -21,6 +21,9 @@
|
||||
#include <tactility/error.h>
|
||||
#include <tactility/filesystem/file_system.h>
|
||||
#include <tactility/module.h>
|
||||
#include <tactility/service/service_instance.h>
|
||||
#include <tactility/service/service_manager.h>
|
||||
#include <tactility/service/service_paths.h>
|
||||
|
||||
#ifndef ESP_PLATFORM
|
||||
#include <tactility/log.h>
|
||||
@@ -247,6 +250,25 @@ const struct ModuleSymbol KERNEL_SYMBOLS[] = {
|
||||
DEFINE_MODULE_SYMBOL(module_is_started),
|
||||
DEFINE_MODULE_SYMBOL(module_resolve_symbol),
|
||||
DEFINE_MODULE_SYMBOL(module_resolve_symbol_global),
|
||||
// service/service_instance
|
||||
DEFINE_MODULE_SYMBOL(service_instance_construct),
|
||||
DEFINE_MODULE_SYMBOL(service_instance_destruct),
|
||||
DEFINE_MODULE_SYMBOL(service_instance_get_manifest),
|
||||
DEFINE_MODULE_SYMBOL(service_instance_get_data),
|
||||
DEFINE_MODULE_SYMBOL(service_instance_get_state),
|
||||
// service/service_manager
|
||||
DEFINE_MODULE_SYMBOL(service_manager_add),
|
||||
DEFINE_MODULE_SYMBOL(service_manager_remove),
|
||||
DEFINE_MODULE_SYMBOL(service_manager_find_manifest),
|
||||
DEFINE_MODULE_SYMBOL(service_manager_start),
|
||||
DEFINE_MODULE_SYMBOL(service_manager_stop),
|
||||
DEFINE_MODULE_SYMBOL(service_manager_get_state),
|
||||
DEFINE_MODULE_SYMBOL(service_manager_find_instance),
|
||||
// service/service_paths
|
||||
DEFINE_MODULE_SYMBOL(service_paths_get_user_data_directory),
|
||||
DEFINE_MODULE_SYMBOL(service_paths_get_user_data_path),
|
||||
DEFINE_MODULE_SYMBOL(service_paths_get_assets_directory),
|
||||
DEFINE_MODULE_SYMBOL(service_paths_get_assets_path),
|
||||
// terminator
|
||||
MODULE_SYMBOL_TERMINATOR
|
||||
};
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#include <tactility/paths.h>
|
||||
#include <tactility/device.h>
|
||||
#include <tactility/drivers/sdcard.h>
|
||||
#include <tactility/filesystem/file_system.h>
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
|
||||
static error_t get_user_data_root_path(char* out_path, size_t out_path_size) {
|
||||
#if defined(CONFIG_TT_USER_DATA_LOCATION_INTERNAL)
|
||||
#ifdef ESP_PLATFORM
|
||||
const char* mount_point = "/data";
|
||||
#else
|
||||
const char* mount_point = "data";
|
||||
#endif
|
||||
if (std::strlen(mount_point) + 1 > out_path_size) {
|
||||
return ERROR_BUFFER_OVERFLOW;
|
||||
}
|
||||
std::strcpy(out_path, mount_point);
|
||||
return ERROR_NONE;
|
||||
#elif defined(CONFIG_TT_USER_DATA_LOCATION_SD)
|
||||
struct FileSystem* found = nullptr;
|
||||
file_system_for_each(&found, [](FileSystem* fs, void* context) {
|
||||
auto* owner = file_system_get_owner(fs);
|
||||
if (owner == nullptr || device_get_type(owner) != &SDCARD_TYPE) {
|
||||
return true;
|
||||
}
|
||||
*static_cast<FileSystem**>(context) = fs;
|
||||
return false;
|
||||
});
|
||||
if (found == nullptr) {
|
||||
return ERROR_NOT_FOUND;
|
||||
}
|
||||
return file_system_get_path(found, out_path, out_path_size);
|
||||
#else
|
||||
#error CONFIG_TT_USER_DATA_* not set or unsupported
|
||||
#endif
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
|
||||
error_t paths_get_user_data_path(char* out_path, size_t out_path_size) {
|
||||
#ifdef ESP_PLATFORM
|
||||
char root[64];
|
||||
error_t error = get_user_data_root_path(root, sizeof(root));
|
||||
if (error != ERROR_NONE) {
|
||||
return error;
|
||||
}
|
||||
int written = std::snprintf(out_path, out_path_size, "%s/tactility", root);
|
||||
if (written < 0 || (size_t)written >= out_path_size) {
|
||||
return ERROR_BUFFER_OVERFLOW;
|
||||
}
|
||||
return ERROR_NONE;
|
||||
#else
|
||||
const char* fixed_path = "data";
|
||||
if (std::strlen(fixed_path) + 1 > out_path_size) {
|
||||
return ERROR_BUFFER_OVERFLOW;
|
||||
}
|
||||
std::strcpy(out_path, fixed_path);
|
||||
return ERROR_NONE;
|
||||
#endif
|
||||
}
|
||||
|
||||
} // extern "C"
|
||||
@@ -0,0 +1,76 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#include <tactility/service/service_instance.h>
|
||||
#include <tactility/concurrent/mutex.h>
|
||||
#include <tactility/log.h>
|
||||
|
||||
#include <new>
|
||||
|
||||
#define TAG "service_instance"
|
||||
|
||||
struct ServiceInstanceInternal {
|
||||
Mutex mutex {};
|
||||
ServiceState state = SERVICE_STATE_STOPPED;
|
||||
};
|
||||
|
||||
extern "C" {
|
||||
|
||||
error_t service_instance_construct(ServiceInstance* instance, const ServiceManifest* manifest) {
|
||||
instance->internal = new(std::nothrow) ServiceInstanceInternal;
|
||||
if (instance->internal == nullptr) {
|
||||
return ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
mutex_construct(&instance->internal->mutex);
|
||||
|
||||
instance->manifest = manifest;
|
||||
instance->data = manifest->create_service(manifest);
|
||||
|
||||
LOG_D(TAG, "construct %s", manifest->id);
|
||||
return ERROR_NONE;
|
||||
}
|
||||
|
||||
error_t service_instance_destruct(ServiceInstance* instance) {
|
||||
auto* internal = instance->internal;
|
||||
|
||||
mutex_lock(&internal->mutex);
|
||||
if (internal->state != SERVICE_STATE_STOPPED) {
|
||||
mutex_unlock(&internal->mutex);
|
||||
return ERROR_INVALID_STATE;
|
||||
}
|
||||
mutex_unlock(&internal->mutex);
|
||||
|
||||
LOG_D(TAG, "destruct %s", instance->manifest->id);
|
||||
|
||||
instance->manifest->destroy_service(instance->manifest, instance->data);
|
||||
instance->data = nullptr;
|
||||
|
||||
instance->internal = nullptr;
|
||||
mutex_destruct(&internal->mutex);
|
||||
delete internal;
|
||||
|
||||
return ERROR_NONE;
|
||||
}
|
||||
|
||||
const ServiceManifest* service_instance_get_manifest(ServiceInstance* instance) {
|
||||
return instance->manifest;
|
||||
}
|
||||
|
||||
void* service_instance_get_data(ServiceInstance* instance) {
|
||||
return instance->data;
|
||||
}
|
||||
|
||||
ServiceState service_instance_get_state(ServiceInstance* instance) {
|
||||
mutex_lock(&instance->internal->mutex);
|
||||
ServiceState state = instance->internal->state;
|
||||
mutex_unlock(&instance->internal->mutex);
|
||||
return state;
|
||||
}
|
||||
|
||||
/** Internal-only: mutate the state of a service instance. Used by service_registration.cpp. */
|
||||
void service_instance_set_state(ServiceInstance* instance, ServiceState state) {
|
||||
mutex_lock(&instance->internal->mutex);
|
||||
instance->internal->state = state;
|
||||
mutex_unlock(&instance->internal->mutex);
|
||||
}
|
||||
|
||||
} // extern "C"
|
||||
@@ -0,0 +1,200 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#include <tactility/service/service_manager.h>
|
||||
#include <tactility/concurrent/mutex.h>
|
||||
#include <tactility/log.h>
|
||||
|
||||
#include <new>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
#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<std::string, const ServiceManifest*> manifests;
|
||||
Mutex mutex {};
|
||||
|
||||
ManifestLedger() { mutex_construct(&mutex); }
|
||||
~ManifestLedger() { mutex_destruct(&mutex); }
|
||||
};
|
||||
|
||||
struct InstanceLedger {
|
||||
std::unordered_map<std::string, ServiceInstance*> 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"
|
||||
@@ -0,0 +1,62 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#include <tactility/service/service_paths.h>
|
||||
#include <tactility/paths.h>
|
||||
|
||||
#include <cstdio>
|
||||
|
||||
extern "C" {
|
||||
|
||||
error_t service_paths_get_user_data_directory(const char* service_id, char* out_path, size_t out_path_size) {
|
||||
char root[192];
|
||||
error_t error = paths_get_user_data_path(root, sizeof(root));
|
||||
if (error != ERROR_NONE) {
|
||||
return error;
|
||||
}
|
||||
int written = std::snprintf(out_path, out_path_size, "%s/service/%s", root, service_id);
|
||||
if (written < 0 || (size_t)written >= out_path_size) {
|
||||
return ERROR_BUFFER_OVERFLOW;
|
||||
}
|
||||
return ERROR_NONE;
|
||||
}
|
||||
|
||||
error_t service_paths_get_user_data_path(const char* service_id, const char* child_path, char* out_path, size_t out_path_size) {
|
||||
char directory[224];
|
||||
error_t error = service_paths_get_user_data_directory(service_id, directory, sizeof(directory));
|
||||
if (error != ERROR_NONE) {
|
||||
return error;
|
||||
}
|
||||
int written = std::snprintf(out_path, out_path_size, "%s/%s", directory, child_path);
|
||||
if (written < 0 || (size_t)written >= out_path_size) {
|
||||
return ERROR_BUFFER_OVERFLOW;
|
||||
}
|
||||
return ERROR_NONE;
|
||||
}
|
||||
|
||||
error_t service_paths_get_assets_directory(const char* service_id, char* out_path, size_t out_path_size) {
|
||||
char directory[224];
|
||||
error_t error = service_paths_get_user_data_directory(service_id, directory, sizeof(directory));
|
||||
if (error != ERROR_NONE) {
|
||||
return error;
|
||||
}
|
||||
int written = std::snprintf(out_path, out_path_size, "%s/assets", directory);
|
||||
if (written < 0 || (size_t)written >= out_path_size) {
|
||||
return ERROR_BUFFER_OVERFLOW;
|
||||
}
|
||||
return ERROR_NONE;
|
||||
}
|
||||
|
||||
error_t service_paths_get_assets_path(const char* service_id, const char* child_path, char* out_path, size_t out_path_size) {
|
||||
char directory[224];
|
||||
error_t error = service_paths_get_assets_directory(service_id, directory, sizeof(directory));
|
||||
if (error != ERROR_NONE) {
|
||||
return error;
|
||||
}
|
||||
int written = std::snprintf(out_path, out_path_size, "%s/%s", directory, child_path);
|
||||
if (written < 0 || (size_t)written >= out_path_size) {
|
||||
return ERROR_BUFFER_OVERFLOW;
|
||||
}
|
||||
return ERROR_NONE;
|
||||
}
|
||||
|
||||
} // extern "C"
|
||||
Reference in New Issue
Block a user