monitor: Add request-server and request-handler-thread 14/271614/13
authorDongwoo Lee <dwoo08.lee@samsung.com>
Mon, 7 Mar 2022 07:50:11 +0000 (16:50 +0900)
committerDongwoo Lee <dwoo08.lee@samsung.com>
Tue, 8 Mar 2022 06:57:28 +0000 (15:57 +0900)
To provide libpass API via socket, a manager is required to
manage network connections and requests from clients.

Request-server is a manager to manage network connections.
It accepts and establishes a socket connection from each
client, then spawns a per-client request-handler-thread to
deal with incoming requests from the client if required.

Change-Id: I17cdf1fde69022663c2369b55982ac43e5b5c688
Signed-off-by: Sung-hun Kim <sfoon.kim@samsung.com>
Signed-off-by: Chanwoo Choi <cw00.choi@samsung.com>
Signed-off-by: Dongwoo Lee <dwoo08.lee@samsung.com>
CMakeLists.txt
include/util/request-handler.h [new file with mode: 0644]
include/util/request.h [new file with mode: 0644]
src/monitor/request-handler.c [new file with mode: 0644]
src/monitor/request-server.c [new file with mode: 0644]

index 9a04a81..b100c63 100644 (file)
@@ -51,6 +51,8 @@ SET(SRCS
        src/monitor/monitor.c
        src/monitor/monitor-thread.c
        src/monitor/monitor-command.c
+       src/monitor/request-server.c
+       src/monitor/request-handler.c
 )
 
 INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR})
diff --git a/include/util/request-handler.h b/include/util/request-handler.h
new file mode 100644 (file)
index 0000000..87f8d0e
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * 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.
+ */
+#ifndef __REQUEST_HANDLER_H__
+#define __REQUEST_HANDLER_H__
+
+#include <glib.h>
+#include <string.h>
+
+#include <util/resource.h>
+
+extern const char *request_type_str[];
+
+struct request_client {
+       int id;
+       int nr_resources;
+       struct thread *worker;
+       GHashTable *resource_table;
+       GMutex mutex;
+};
+
+#define REQUEST_BUFFER_MAX 1000
+
+int create_request_client(int socket_fd);
+
+#endif /* __REQUEST_HANDLER_H__ */
diff --git a/include/util/request.h b/include/util/request.h
new file mode 100644 (file)
index 0000000..47b30cf
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * 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.
+ */
+#ifndef __REQUEST_H__
+#define __REQUEST_H__
+
+/*
+ * request format
+ * REQUEST_TYPE:REQUEST_CONTENT(if required)
+ */
+
+enum {
+       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,
+       REQUEST_MAX,
+};
+
+#endif /* __REQUEST_H__ */
+
diff --git a/src/monitor/request-handler.c b/src/monitor/request-handler.c
new file mode 100644 (file)
index 0000000..8082e81
--- /dev/null
@@ -0,0 +1,347 @@
+/*
+ * 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;
+}
+
diff --git a/src/monitor/request-server.c b/src/monitor/request-server.c
new file mode 100644 (file)
index 0000000..27a580e
--- /dev/null
@@ -0,0 +1,184 @@
+/*
+ * 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);