--- /dev/null
+/*
+ * PASS (Power Aware System Service)
+ *
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * @file request-handler-thread.c
+ * @brief TBD
+ * @ingroup TBD
+ */
+
+#include <glib.h>
+
+#include <util/common.h>
+#include <util/log.h>
+#include <util/resource.h>
+#include <util/thread.h>
+#include <util/device-notifier.h>
+#include <util/devices.h>
+#include <util/request.h>
+#include <util/request-handler.h>
+
+#include <string.h>
+#include <arpa/inet.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <sys/time.h>
+#include <assert.h>
+
+static void free_resource(gpointer key, gpointer value, gpointer user_data)
+{
+ struct resource *res = value;
+
+ delete_resource(res);
+}
+
+static void finalize_request_client(struct request_client *client)
+{
+ if (!client)
+ return;
+
+ if (client->resource_table) {
+ g_mutex_lock(&client->mutex);
+ g_hash_table_foreach(client->resource_table, (GHFunc)free_resource, NULL);
+ g_mutex_unlock(&client->mutex);
+ }
+}
+
+static void
+register_resource_to_client(struct request_client *client, struct resource *res)
+{
+ g_mutex_lock(&client->mutex);
+ g_hash_table_insert(client->resource_table, (gpointer)&res->id, (gpointer)res);
+ g_mutex_unlock(&client->mutex);
+}
+
+static struct resource *
+get_resource_by_id(struct request_client *client, int resource_id)
+{
+ struct resource *res;
+
+ g_mutex_lock(&client->mutex);
+ res = g_hash_table_lookup(client->resource_table, (gpointer)&resource_id);
+ g_mutex_unlock(&client->mutex);
+
+ return res;
+}
+
+static int handle_request_create_resource(struct request_client *client, char *args)
+{
+ struct resource *res;
+ int resource_type;
+
+ if (!client || !args)
+ return -ENOENT;
+
+ /**
+ * Format of REQUEST_CREATE_RESOURCE args:
+ * - <RESOURCE_TYPE>
+ */
+ resource_type = atoi(args);
+
+ res = create_resource(resource_type);
+ if (!res)
+ return -EINVAL;
+
+ register_resource_to_client(client, res);
+
+ return res->id;
+}
+
+static int handle_request_update_resource(struct request_client *client, char *args)
+{
+ struct resource *res;
+ int resource_id;
+
+ if (!client || !args)
+ return -ENOENT;
+
+ /**
+ * Format of REQUEST_UPDATE_RESOURCE args:
+ * - <RESOURCE_ID>
+ */
+ resource_id = atoi(args);
+
+ res = get_resource_by_id(client, resource_id);
+ if (!res)
+ return -EINVAL;
+
+ return update_resource_attrs(res);
+}
+
+static int handle_request_set_attr_interest(struct request_client *client, char *args)
+{
+ struct resource *res;
+ int resource_id;
+ u_int64_t interest_masks;
+
+ if (!client || !args)
+ return -ENOENT;
+
+ /**
+ * Format of REQUEST_SET_ATTR_INTEREST args:
+ * - <RESOURCE_ID:INTEREST_MASK>
+ */
+ if (sscanf(args, "%d:%"PRIu64, &resource_id, &interest_masks) < 2)
+ return -EINVAL;
+
+ res = get_resource_by_id(client, resource_id);
+ if (!res)
+ return -EINVAL;
+
+ set_resource_attr_interest(res, interest_masks);
+
+ return 0;
+}
+
+static int handle_request_set_resource_control(struct request_client *client, char *args)
+{
+ struct resource *res;
+ int resource_id, value;
+ u_int64_t ctrl_id;
+
+ if (!client || !args)
+ return -ENOENT;
+
+ /**
+ * Format of REQUEST_SET_RESOURCE_CONTROL args:
+ * - <RESOURCE_ID:CONTROL_ID:CONTROL_VALUE>
+ */
+ if (sscanf(args, "%d:%"PRIu64":%d", &resource_id, &ctrl_id, &value) < 3)
+ return -EINVAL;
+
+ res = get_resource_by_id(client, resource_id);
+ if (!res)
+ return -EINVAL;
+
+ return set_resource_control(res, ctrl_id, (void *)(intptr_t)value);
+}
+
+static int handle_request_get_value_int(struct request_client *client, char *args, int32_t *value)
+{
+ struct resource *res;
+ int resource_id;
+ u_int64_t attr_id;
+
+ if (!client || !args)
+ return -ENOENT;
+
+ /**
+ * Format of REQUEST_GET_VALUE_INT args:
+ * - <RESOURCE_ID:ATTR_ID>
+ */
+ if (sscanf(args, "%d:%"PRIu64, &resource_id, &attr_id) < 2)
+ return -EINVAL;
+
+ res = get_resource_by_id(client, resource_id);
+ if (!res)
+ return -EINVAL;
+
+ return get_resource_attr_int(res, attr_id, value);
+}
+
+static int split_request_type_and_args(char *buffer, char **args)
+{
+ char *request_type_str;
+
+ request_type_str = strsep(&buffer, ":");
+
+ *args = buffer;
+
+ return atoi(request_type_str);
+}
+
+static void handle_request(struct request_client *client, char *buffer)
+{
+ char _response[REQUEST_BUFFER_MAX];
+ char *response = _response;
+ char *args;
+ int request_type;
+ int ret, len;
+
+ request_type = split_request_type_and_args(buffer, &args);
+
+ /**
+ * Format of response
+ * - <REQUEST_TYPE[:REQUEST_RESULT_PAYLOAD]:REQUEST_RESULT_VALUE>
+ */
+ len = sprintf(response, "%d:", request_type);
+ response += len;
+
+ switch (request_type) {
+ case REQUEST_CREATE_RESOURCE:
+ ret = handle_request_create_resource(client, args);
+ if (ret < 0)
+ _D("failed to create resource");
+ break;
+ case REQUEST_UPDATE_RESOURCE:
+ ret = handle_request_update_resource(client, args);
+ if (ret < 0)
+ _D("failed to update resource");
+ break;
+ case REQUEST_SET_ATTR_INTEREST:
+ ret = handle_request_set_attr_interest(client, args);
+ if (ret < 0)
+ _D("failed to set attr interest");
+ break;
+ case REQUEST_SET_RESOURCE_CONTROL:
+ ret = handle_request_set_resource_control(client, args);
+ if (ret < 0)
+ _D("failed to set resource control");
+ break;
+ case REQUEST_GET_VALUE_INT:
+ {
+ int32_t value;
+
+ ret = handle_request_get_value_int(client, args, &value);
+ if (ret < 0)
+ _D("failed to get value");
+
+ len = sprintf(response, "%d:", value);
+ response += len;
+ }
+ break;
+ default:
+ _E("Invliad request type: %d", request_type);
+ ret = -EINVAL;
+ break;
+ }
+ sprintf(response, "%d", ret);
+
+ if (send(client->id, _response, strlen(_response), 0) < 0)
+ _E("Failed to send respones, error: %s", strerror(errno));
+}
+
+static GList *g_request_client_head;
+static GMutex g_server_lock;
+
+static void add_client_to_list(struct request_client *client)
+{
+ if (!client)
+ return;
+
+ g_mutex_lock(&g_server_lock);
+ g_request_client_head =
+ g_list_append(g_request_client_head, (gpointer)client);
+ g_mutex_unlock(&g_server_lock);
+}
+
+static void remove_client_from_list(struct request_client *client)
+{
+ if (!client)
+ return;
+
+ g_mutex_lock(&g_server_lock);
+ g_request_client_head =
+ g_list_remove(g_request_client_head, (gpointer)client);
+ g_mutex_unlock(&g_server_lock);
+}
+
+static void destroy_request_client(struct request_client *client)
+{
+ remove_client_from_list(client);
+ free(client);
+}
+
+static int request_handler_func(void *data, void **result)
+{
+ char buffer[REQUEST_BUFFER_MAX + 1];
+ struct request_client *client = (struct request_client *)data;
+ int len;
+
+ _D("Start worker thread for client-%d", client->id);
+
+ while (1) {
+ len = recv(client->id, buffer, REQUEST_BUFFER_MAX, 0);
+ if (len == 0) {
+ _D("Client-%d is disconnected", client->id);
+ finalize_request_client(client);
+ close(client->id);
+ destroy_request_client(client);
+ return THREAD_RETURN_DONE;
+ }
+
+ buffer[len] = '\0';
+ handle_request(client, buffer);
+ }
+
+ return THREAD_RETURN_DONE;
+}
+
+int create_request_client(int socket_fd)
+{
+ struct request_client *client;
+
+ client = malloc(sizeof(struct request_client));
+ if (!client) {
+ _E("Failed to allocate memory of request_client");
+ return -ENOMEM;
+ }
+
+ client->id = socket_fd;
+ client->resource_table = g_hash_table_new(g_int_hash, g_int_equal);
+
+ g_mutex_init(&client->mutex);
+
+ create_daemon_thread(&client->worker, request_handler_func, client);
+
+ add_client_to_list(client);
+
+ return 0;
+}
+
--- /dev/null
+/*
+ * PASS (Power Aware System Service)
+ *
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * @file request-server.c
+ * @brief TBD
+ * @ingroup TBD
+ */
+
+#include <glib.h>
+
+#include <util/common.h>
+#include <util/log.h>
+#include <util/resource.h>
+#include <util/thread.h>
+#include <util/device-notifier.h>
+#include <util/devices.h>
+#include <util/request.h>
+#include <util/request-handler.h>
+
+#include <string.h>
+#include <arpa/inet.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <sys/time.h>
+#include <assert.h>
+
+#define PENDING_MAX 3
+#define REQUEST_SERVER_PORT 10001
+
+static bool g_request_server_run;
+
+const char *request_type_str[] = {
+ "REQUEST_CREATE_RESOURCE",
+ "REQUEST_UPDATE_RESOURCE",
+ "REQUEST_REMOVE_RESOURCE",
+ "REQUEST_SET_ATTR_INTEREST",
+ "REQUEST_UNSET_ATTR_INTEREST",
+ "REQUEST_SET_RESOURCE_CONTROL",
+ "REQUEST_GET_VALUE_INT",
+ "REQUEST_GET_RESOURCE_NUM",
+ "REQUEST_GET_AVAILABLE_ATTRS",
+};
+
+static int request_server_func(void *ctx, void **result)
+{
+ struct sockaddr_in address;
+ struct timeval wait;
+ int opt = true;
+ int server_socket;
+ int addrlen;
+ int ret;
+ fd_set fds;
+
+ if (!g_request_server_run)
+ return THREAD_RETURN_DONE;
+
+ /* 0. initialize server socket */
+ server_socket = socket(AF_INET, SOCK_STREAM, 0);
+ if (server_socket < 0) {
+ _E("Failed to initialize socket");
+ goto error_out;
+ }
+
+ if (setsockopt(server_socket, SOL_SOCKET, SO_REUSEADDR,
+ (char *)&opt, sizeof(opt)) < 0) {
+ _E("Failed to setsockopt");
+ goto error_out_close;
+ }
+
+ address.sin_family = AF_INET;
+ address.sin_addr.s_addr = INADDR_ANY;
+ address.sin_port = htons(REQUEST_SERVER_PORT);
+
+ if (bind(server_socket, (struct sockaddr *)&address, sizeof(address)) < 0) {
+ _E("Failed to bind");
+ goto error_out_close;
+ }
+
+ if (listen(server_socket, PENDING_MAX) < 0) {
+ _E("Failed to begin listenning");
+ goto error_out_close;
+ }
+
+ addrlen = sizeof(address);
+
+ while (g_request_server_run) {
+ FD_ZERO(&fds);
+ FD_SET(server_socket, &fds);
+
+ wait.tv_sec = 1;
+ wait.tv_usec = 0;
+
+ ret = select(FD_SETSIZE, &fds, NULL, NULL, &wait);
+ if (ret < 0) {
+ _E("failed to select");
+ goto error_out_close;
+ }
+
+ if (FD_ISSET(server_socket, &fds)) {
+ int new_socket = accept(server_socket, (struct sockaddr *)&address,
+ (socklen_t *)&addrlen);
+
+ if (new_socket < 0) {
+ _E("Failed to accept");
+ goto error_out_close;
+ }
+
+ create_request_client(new_socket);
+ }
+ }
+
+ close(server_socket);
+
+ return THREAD_RETURN_DONE;
+
+error_out_close:
+ close(server_socket);
+error_out:
+
+ return THREAD_RETURN_ERROR;
+}
+
+static struct thread *server_thread;
+
+static int request_server_init_done(void *data, void *user_data)
+{
+ g_request_server_run = true;
+ if (create_daemon_thread(&server_thread, request_server_func, NULL))
+ _E("Failed to create request_server_thread ");
+
+ return 0;
+}
+
+static void request_server_init(void *data)
+{
+ /* nothing to do */
+}
+
+static void request_server_exit(void *data)
+{
+ unregister_notifier(DEVICE_NOTIFIER_INIT_DONE, request_server_init_done, NULL);
+ g_request_server_run = false;
+ destroy_thread(server_thread);
+}
+
+static int request_server_probe(void *data)
+{
+ int ret = 0;
+
+ ret = register_notifier(DEVICE_NOTIFIER_INIT_DONE,
+ request_server_init_done, NULL);
+ if (ret < 0) {
+ _E("Failed to register a callback function");
+ return ret;
+ }
+
+ return 0;
+}
+
+static const struct device_ops request_server_device_ops = {
+ .name = "request-server",
+ .probe = request_server_probe,
+ .init = request_server_init,
+ .exit = request_server_exit,
+};
+
+DEVICE_OPS_REGISTER(&request_server_device_ops);