From: Dongwoo Lee Date: Mon, 7 Mar 2022 07:50:11 +0000 (+0900) Subject: monitor: Add request-server and request-handler-thread X-Git-Tag: accepted/tizen/unified/20220313.205607~16 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=refs%2Fchanges%2F14%2F271614%2F13;p=platform%2Fcore%2Fsystem%2Fpass.git monitor: Add request-server and request-handler-thread 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 Signed-off-by: Chanwoo Choi Signed-off-by: Dongwoo Lee --- diff --git a/CMakeLists.txt b/CMakeLists.txt index 9a04a81..b100c63 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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 index 0000000..87f8d0e --- /dev/null +++ b/include/util/request-handler.h @@ -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 +#include + +#include + +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 index 0000000..47b30cf --- /dev/null +++ b/include/util/request.h @@ -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 index 0000000..8082e81 --- /dev/null +++ b/src/monitor/request-handler.c @@ -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 + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +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 = 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 = 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: + * - + */ + 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: + * - + */ + 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: + * - + */ + 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 + * - + */ + 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 index 0000000..27a580e --- /dev/null +++ b/src/monitor/request-server.c @@ -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 + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#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);