resource: request server dev
authorSung-hun Kim <sfoon.kim@samsung.com>
Mon, 7 Feb 2022 05:43:33 +0000 (14:43 +0900)
committerSung-hun Kim <sfoon.kim@samsung.com>
Mon, 21 Feb 2022 00:45:52 +0000 (09:45 +0900)
Change-Id: Ice7e95dc09290b0ecef7d18d7afc47e2826971a0
Signed-off-by: Sung-hun Kim <sfoon.kim@samsung.com>
CMakeLists.txt
include/util/request.h [new file with mode: 0644]
src/resource/request-server.c [new file with mode: 0644]

index f30a5eb6a2086e055367aa1e2b31b2d21964c37e..b6379d725f5e390184804ea10822a6d157d5f49c 100644 (file)
@@ -47,6 +47,7 @@ SET(SRCS
        src/monitor/monitor.c
        src/monitor/monitor-thread.c
        src/monitor/monitor-command.c
+       src/resource/request-server.c
 )
 
 INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR})
diff --git a/include/util/request.h b/include/util/request.h
new file mode 100644 (file)
index 0000000..c244b8f
--- /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_INIT,
+       REQUEST_EXIT,
+       REQUEST_SET_ATTRS,
+       REQUEST_GET_AVAILABLE_ATTRS,
+       REQUEST_START,
+       REQUEST_STOP,
+       REQUEST_UPDATE,
+       REQUEST_GET_VALUE_INT,
+       REQUEST_GET_RESOURCE_NUM,
+       REQUEST_TYPE,
+};
+
+#endif /* __REQUEST_H__ */
+
diff --git a/src/resource/request-server.c b/src/resource/request-server.c
new file mode 100644 (file)
index 0000000..1c6e986
--- /dev/null
@@ -0,0 +1,447 @@
+/*
+ * 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 <string.h>
+#include <arpa/inet.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <sys/time.h>
+
+#define CLIENT_MAX 100
+#define PENDING_MAX 3
+#define REQUEST_MAX 1000
+
+#define REQUEST_SERVER_PORT 10001
+
+static bool request_server_run;
+
+const char *request_type_str[] = {
+       "REQUEST_INIT",
+       "REQUEST_EXIT",
+       "REQUEST_SET_ATTRS",
+       "REQUEST_GET_AVAILABLE_ATTRS",
+       "REQUEST_START",
+       "REQUEST_STOP",
+       "REQUEST_UPDATE",
+       "REQUEST_GET_VALUE_INT",
+       "REQUEST_GET_RESOURCE_NUM",
+       "REQUEST_TYPE",
+};
+
+/*
+ * states of client
+ */
+enum {
+       CLIENT_DISCONNECTED,
+       CLIENT_CONNECTED,
+       CLIENT_RUNNING,
+};
+
+struct request_client {
+       int id; // unique identifier
+       int state; // one of three states (DISCONNECTED, CONNECTED, RUNNING)
+       int socket_fd; // socket
+       int period; // period in millisecond (0 means one-shot)
+};
+
+static struct request_client clients[CLIENT_MAX];
+
+static GList *g_request_client_head;
+
+static unsigned int client_id;
+
+static void handle_request_in_disconnected(struct request_client *client, char *buffer)
+{
+       char *tok;
+       int request_type;
+       char response[100];
+       int response_len;
+
+       tok = strtok(buffer, ":");
+       request_type = atoi(tok);
+       tok = strtok(NULL, ":");
+       _I("%d: %s", request_type, tok);
+
+       /* expect init call */
+       if (request_type != REQUEST_INIT) {
+               _E("state: %d Invalid request type: %s", client->state,
+                               request_type_str[request_type]);
+               return;
+       }
+       client->state = CLIENT_CONNECTED;
+       client->period = atoi(tok); // second token should be a period value
+       _I("state: %d request_type: %s period: %d", client->state,
+                       request_type_str[request_type], client->period);
+
+       /* send response */
+       response_len = sprintf(response, "%d:%d", REQUEST_INIT, client->id);
+       if (send(client->socket_fd, response, response_len, 0) < 0) {
+               _E("failed to send response error: %s", strerror(errno));
+               return;
+       }
+       _I("send response \"%s\" to client-%d", response, client->id);
+}
+
+static void handle_request_in_connected(struct request_client *client, char *buffer)
+{
+       char *tok;
+       int request_type;
+       char response[100];
+       int response_len;
+
+       tok = strtok(buffer, ":");
+       request_type = atoi(tok);
+       tok = strtok(NULL, ":");
+       _I("%d: %s", request_type, tok);
+
+       /* expect start, set_attrs, exit call */
+       if (request_type != REQUEST_START && request_type != REQUEST_SET_ATTRS
+                       && request_type != REQUEST_EXIT) {
+               _E("state: %d Invalid request type: %s client-%d", client->state,
+                               request_type_str[request_type], client->id);
+               return;
+       }
+       _I("state: %d request_type: %s", client->state, request_type_str[request_type]);
+}
+
+static void handle_request_in_running(struct request_client *client, char *buffer)
+{
+       char *tok;
+       int request_type;
+       char response[100];
+       int response_len;
+
+       tok = strtok(buffer, ":");
+       request_type = atoi(tok);
+       tok = strtok(NULL, ":");
+       _I("%d: %s", request_type, tok);
+
+       /* expect stop call */
+       if (request_type != REQUEST_STOP) {
+               _E("state: %d Invalid request type: %s", client->state, request_type_str[request_type]);
+               return;
+       }
+       _I("state: %d request_type: %s", client->state, request_type_str[request_type]);
+}
+
+static void handle_request(struct request_client *client, char *buffer)
+{
+       switch (client->state) {
+       case CLIENT_DISCONNECTED:
+               handle_request_in_disconnected(client, buffer);
+               break;
+
+       case CLIENT_CONNECTED:
+               handle_request_in_connected(client, buffer);
+               break;
+
+       case CLIENT_RUNNING:
+               handle_request_in_running(client, buffer);
+               break;
+       }
+}
+
+static void add_client_to_list(struct request_client *client)
+{
+       if (!client)
+               return;
+
+       g_request_client_head =
+                       g_list_append(g_request_client_head, (gpointer)client);
+}
+
+static void remove_client_from_list(struct request_client *client)
+{
+       if (!client)
+               return;
+
+       g_request_client_head =
+                       g_list_remove(g_request_client_head, (gpointer)client);
+}
+
+static void client_list_test()
+{
+       int i;
+       GList *node;
+       struct request_client *clients[100];
+       int sequence[100] = {30, 4, 92, 19, 45, 63, 21, 98, 72, 56, 52, 44, 73, 41,
+               60, 29, 7, 15, 86, 8, 75, 0, 17, 68, 66, 10, 47, 49, 91, 18, 89, 20, 40,
+               32, 36, 83, 59, 87, 71, 76, 13, 25, 64, 84, 24, 55, 43, 50, 79, 23, 11,
+               61, 9, 34, 5, 96, 39, 46, 80, 42, 38, 53, 90, 94, 74, 51, 82, 48, 1, 77,
+               88, 95, 26, 70, 81, 85, 35, 16, 22, 57, 14, 37, 3, 54, 58, 12, 67, 97, 93,
+               27, 99, 62, 78, 28, 6, 31, 33, 2, 65, 69};
+
+       for (i = 0; i < 100; i++) {
+               clients[i] = malloc(sizeof(struct request_client));
+               add_client_to_list(clients[i]);
+       }
+       node = g_request_client_head;
+       while (node != NULL) {
+               struct request_client *client = node->data;
+               GList *next = node->next;
+               remove_client_from_list(client);
+               free(client);
+               node = next;
+       }
+       _I("after sequential test, nr_nodes: %d", g_list_length(g_request_client_head));
+
+       for (i = 0; i < 100; i++) {
+               clients[i] = malloc(sizeof(struct request_client));
+               add_client_to_list(clients[i]);
+       }
+
+       for (i = 0; i < 100; i++) {
+               remove_client_from_list(clients[sequence[i]]);
+               free(clients[sequence[i]]);
+       }
+       _I("after random test, nr_nodes: %d", g_list_length(g_request_client_head));
+}
+
+static int request_server_func(void *ctx, void **result)
+{
+       char buffer[REQUEST_MAX];
+       int opt = true;
+       int server_socket;
+       int addrlen;
+       int new_socket;
+       int i;
+       int read_len;
+       struct sockaddr_in address;
+       struct timeval wait;
+       struct request_client *client;
+       struct request_client *temp;
+       fd_set read_fds;
+       fd_set active_fds;
+
+       if (!request_server_run)
+               return THREAD_RETURN_DONE;
+       /*
+       for (i = 0; i < CLIENT_MAX; i++) {
+               clients[i].state = CLIENT_DISCONNECTED;
+               clients[i].socket_fd = 0;
+       }
+       */
+
+       /* 0. initialize server socket */
+       server_socket = socket(AF_INET, SOCK_STREAM, 0);
+       if (server_socket == 0) {
+               _E("socket failed");
+               goto error_out;
+       }
+
+       if (setsockopt(server_socket, SOL_SOCKET, SO_REUSEADDR,
+                       (char *)&opt, sizeof(opt)) < 0) {
+               _E("setsockopt failed");
+               goto error_out;
+       }
+
+       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("bind failed");
+               goto error_out;
+       }
+
+       if (listen(server_socket, PENDING_MAX) < 0) {
+               _E("listen failed");
+               goto error_out;
+       }
+
+       addrlen = sizeof(address);
+
+       FD_ZERO(&active_fds);
+       FD_SET(server_socket, &active_fds);
+
+       wait.tv_sec = 1;
+       wait.tv_usec = 0;
+       _I("start server");
+
+       client_list_test();
+
+       _I("after client list test");
+
+       while (request_server_run) {
+               /* 1. (re)initialize client sockets */
+               read_fds = active_fds;
+
+               if (select(FD_SETSIZE, &read_fds, NULL, NULL, &wait) < 0) {
+                       _E("select failed");
+                       goto error_out;
+               }
+
+               /* 2. accept a new socket connection */
+               if (FD_ISSET(server_socket, &read_fds)) {
+                       new_socket = accept(server_socket, (struct sockaddr *)&address,
+                                       (socklen_t*)&addrlen);
+                       if (new_socket < 0) {
+                               _E("accept error");
+                               goto error_out;
+                       }
+                       _I("new connection, socket fd is %d, ip is %s, port : %d\n",
+                                       new_socket, inet_ntoa(address.sin_addr),
+                                       ntohs(address.sin_port));
+                       FD_SET(new_socket, &active_fds);
+
+                       client = malloc(sizeof(struct request_client));
+                       client->state = CLIENT_DISCONNECTED;
+                       client->socket_fd = new_socket;
+                       client->id = client_id++;
+                       add_client_to_list(client);
+
+                       _I("adding to list of sockets: client-%d\n", client->id);
+
+                       /*
+                       for (i = 0; i < CLIENT_MAX; i++) {
+                               if (clients[i].state == CLIENT_DISCONNECTED) {
+                                       clients[i].socket_fd = new_socket;
+                                       FD_SET(new_socket, &active_fds);
+                                       _I("adding to list of sockets as %d\n", i);
+                                       break;
+                               }
+                       }
+                       */
+               }
+
+               /* 3. receive requests */
+               GList *node = g_request_client_head;
+               while (node != NULL) {
+                       GList *next = node->next;
+                       client = node->data;
+                       int sd = client->socket_fd;
+                       if (FD_ISSET(sd, &read_fds)) {
+                               read_len = read(sd, buffer, REQUEST_MAX);
+                               if (read_len == 0) {
+                                       getpeername(sd, (struct sockaddr *)&address,
+                                                       (socklen_t *)&addrlen);
+                                       _I("host disconnected, ip %s, port %d",
+                                                       inet_ntoa(address.sin_addr), ntohs(address.sin_port));
+                                       close(sd);
+                                       FD_CLR(sd, &active_fds);
+                                       remove_client_from_list(client);
+                                       free(client);
+                               } else {
+                                       buffer[read_len] = '\0';
+                                       _I("incoming buffer: %s", buffer);
+                                       handle_request(client, buffer);
+                               }
+                       }
+                       node = next;
+               }
+               /*
+               for (i = 0; i < CLIENT_MAX; i++) {
+                       int sd = clients[i].socket_fd;
+                       if (FD_ISSET(sd, &read_fds)) {
+                               read_len = read(sd, buffer, REQUEST_MAX);
+                               if (read_len == 0) {
+                                       getpeername(sd, (struct sockaddr *)&address,
+                                                       (socklen_t *)&addrlen);
+                                       _I("host disconnected, ip %s, port %d",
+                                                       inet_ntoa(address.sin_addr), ntohs(address.sin_port));
+                                       close(sd);
+                                       FD_CLR(sd, &active_fds);
+                                       clients[i].socket_fd = 0;
+                                       clients[i].state = CLIENT_DISCONNECTED;
+                                       continue;
+                               }
+
+                               buffer[read_len] = '\0';
+                               _I("incoming buffer: %s", buffer);
+                               handle_request(&clients[i], buffer);
+                       }
+               }
+               */
+       }
+
+       close(server_socket);
+
+       return THREAD_RETURN_DONE;
+
+error_out:
+       close(server_socket);
+
+       return THREAD_RETURN_ERROR;
+}
+
+static struct thread *server_thread;
+
+static int request_server_init_done(void *data, void *user_data)
+{
+       request_server_run = true;
+       if (create_daemon_thread(&server_thread, request_server_func, NULL)) {
+               _E("error occurred while creating request_server_thread ");
+       }
+
+       return 0;
+}
+
+static void request_server_init(void *data)
+{
+       /* nothing to do */
+}
+
+static void request_server_exit(void *data)
+{
+       void *dummy;
+
+       unregister_notifier(DEVICE_NOTIFIER_INIT_DONE, request_server_init_done, NULL);
+       request_server_run = false;
+       wait_for_completion(server_thread, &dummy);
+       _E("after completion");
+}
+
+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("cannot 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);