--- /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 <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);