2 * Copyright 2013 Samsung Electronics Co., Ltd
4 * Licensed under the Flora License, Version 1.1 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://floralicense.org/license/
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
19 #include <secure_socket.h>
23 #include <sys/types.h>
24 #include <sys/timerfd.h>
29 #include "service_common.h"
32 #define EVT_END_CH 'x'
36 struct service_event_item {
47 int (*event_cb)(struct service_context *svc_cx, void *data);
53 * Server information and global (only in this file-scope) variables are defined
55 struct service_context {
56 pthread_t server_thid; /*!< Server thread Id */
57 int fd; /*!< Server socket handle */
59 Eina_List *tcb_list; /*!< TCB list, list of every thread for client connections */
61 Eina_List *packet_list;
62 pthread_mutex_t packet_list_lock;
63 int evt_pipe[PIPE_MAX];
64 int tcb_pipe[PIPE_MAX];
66 int (*service_thread_main)(struct tcb *tcb, struct packet *packet, void *data);
67 void *service_thread_data;
69 Eina_List *event_list;
74 struct packet *packet;
79 * Thread Control Block
80 * - The main server will create a thread for every client connections.
81 * When a new client is comming to us, this TCB block will be allocated and initialized.
83 struct tcb { /* Thread controll block */
84 struct service_context *svc_ctx;
85 pthread_t thid; /*!< Thread Id */
86 int fd; /*!< Connection handle */
91 * Do services for clients
92 * Routing packets to destination processes.
95 static void *client_packet_pump_main(void *data)
97 struct tcb *tcb = data;
98 struct service_context *svc_ctx = tcb->svc_ctx;
99 struct packet *packet;
107 char evt_ch = EVT_CH;
114 struct packet_info *packet_info;
118 recv_state = RECV_INIT;
121 * To escape from the switch statement, we use this ret value
125 FD_SET(tcb->fd, &set);
126 ret = select(tcb->fd + 1, &set, NULL, NULL, NULL);
129 if (errno == EINTR) {
136 } else if (ret == 0) {
143 if (!FD_ISSET(tcb->fd, &set)) {
152 * Service!!! Receive packet & route packet
154 switch (recv_state) {
156 size = packet_header_size();
165 recv_state = RECV_HEADER;
166 /* Go through, don't break from here */
168 ret = secure_socket_recv(tcb->fd, ptr, size - recv_offset, &pid);
180 if (recv_offset == size) {
181 packet = packet_build(packet, packet_offset, ptr, size);
189 packet_offset += recv_offset;
191 size = packet_payload_size(packet);
193 recv_state = RECV_DONE;
198 recv_state = RECV_PAYLOAD;
208 ret = secure_socket_recv(tcb->fd, ptr, size - recv_offset, &pid);
220 if (recv_offset == size) {
221 packet = packet_build(packet, packet_offset, ptr, size);
229 packet_offset += recv_offset;
231 recv_state = RECV_DONE;
241 if (recv_state == RECV_DONE) {
243 * Push this packet to the packet list with TCB
244 * Then the service main function will get this.
246 packet_info = malloc(sizeof(*packet_info));
249 packet_destroy(packet);
253 packet_info->packet = packet;
254 packet_info->tcb = tcb;
256 CRITICAL_SECTION_BEGIN(&svc_ctx->packet_list_lock);
257 svc_ctx->packet_list = eina_list_append(svc_ctx->packet_list, packet_info);
258 CRITICAL_SECTION_END(&svc_ctx->packet_list_lock);
260 if (write(svc_ctx->evt_pipe[PIPE_WRITE], &evt_ch, sizeof(evt_ch)) != sizeof(evt_ch)) {
262 CRITICAL_SECTION_BEGIN(&svc_ctx->packet_list_lock);
263 svc_ctx->packet_list = eina_list_remove(svc_ctx->packet_list, packet_info);
264 CRITICAL_SECTION_END(&svc_ctx->packet_list_lock);
266 packet_destroy(packet);
270 recv_state = RECV_INIT;
275 CRITICAL_SECTION_BEGIN(&svc_ctx->packet_list_lock);
276 EINA_LIST_FOREACH(svc_ctx->packet_list, l, packet_info) {
277 if (packet_info->tcb == tcb) {
278 packet_info->tcb = NULL;
281 CRITICAL_SECTION_END(&svc_ctx->packet_list_lock);
285 * Emit a signal to collect this TCB from the SERVER THREAD.
287 write(svc_ctx->tcb_pipe[PIPE_WRITE], &tcb, sizeof(tcb)) != sizeof(tcb);
296 static inline struct tcb *tcb_create(struct service_context *svc_ctx, int fd)
301 tcb = malloc(sizeof(*tcb));
307 tcb->svc_ctx = svc_ctx;
308 tcb->type = TCB_CLIENT_TYPE_APP;
310 status = pthread_create(&tcb->thid, NULL, client_packet_pump_main, tcb);
316 svc_ctx->tcb_list = eina_list_append(svc_ctx->tcb_list, tcb);
324 static inline void tcb_teminate_all(struct service_context *svc_ctx)
331 * We don't need to make critical section on here.
332 * If we call this after terminate the server thread first.
333 * Then there is no other thread to access tcb_list.
335 EINA_LIST_FREE(svc_ctx->tcb_list, tcb) {
337 * ASSERT(tcb->fd >= 0);
339 secure_socket_destroy_handle(tcb->fd);
341 status = pthread_join(tcb->thid, &ret);
351 static inline void tcb_destroy(struct service_context *svc_ctx, struct tcb *tcb)
356 svc_ctx->tcb_list = eina_list_remove(svc_ctx->tcb_list, tcb);
358 * ASSERT(tcb->fd >= 0);
359 * Close the connection, and then collecting the return value of thread
361 secure_socket_destroy_handle(tcb->fd);
363 status = pthread_join(tcb->thid, &ret);
372 static inline int find_max_fd(struct service_context *svc_ctx)
376 struct service_event_item *item;
378 fd = svc_ctx->fd > svc_ctx->tcb_pipe[PIPE_READ] ? svc_ctx->fd : svc_ctx->tcb_pipe[PIPE_READ];
379 fd = fd > svc_ctx->evt_pipe[PIPE_READ] ? fd : svc_ctx->evt_pipe[PIPE_READ];
381 EINA_LIST_FOREACH(svc_ctx->event_list, l, item) {
382 if (item->type == SERVICE_EVENT_TIMER && fd < item->info.timer.fd)
383 fd = item->info.timer.fd;
394 static inline void update_fdset(struct service_context *svc_ctx, fd_set *set)
397 struct service_event_item *item;
400 FD_SET(svc_ctx->fd, set);
401 FD_SET(svc_ctx->tcb_pipe[PIPE_READ], set);
402 FD_SET(svc_ctx->evt_pipe[PIPE_READ], set);
404 EINA_LIST_FOREACH(svc_ctx->event_list, l, item) {
405 if (item->type == SERVICE_EVENT_TIMER)
406 FD_SET(item->info.timer.fd, set);
414 static inline void processing_timer_event(struct service_context *svc_ctx, fd_set *set)
416 uint64_t expired_count;
419 struct service_event_item *item;
421 EINA_LIST_FOREACH_SAFE(svc_ctx->event_list, l, n, item) {
422 switch (item->type) {
423 case SERVICE_EVENT_TIMER:
424 if (!FD_ISSET(item->info.timer.fd, set))
427 if (read(item->info.timer.fd, &expired_count, sizeof(expired_count)) == sizeof(expired_count)) {
428 if (item->event_cb(svc_ctx, item->cbdata) >= 0)
432 if (!eina_list_data_find(svc_ctx->event_list, item))
435 svc_ctx->event_list = eina_list_remove(svc_ctx->event_list, item);
436 close(item->info.timer.fd);
447 * Accept new client connections
448 * And create a new thread for service.
450 * Create Client threads & Destroying them
453 static void *server_main(void *data)
455 struct service_context *svc_ctx = data;
462 struct packet_info *packet_info;
465 fd = find_max_fd(svc_ctx);
466 update_fdset(svc_ctx, &set);
468 ret = select(fd, &set, NULL, NULL, NULL);
471 if (errno == EINTR) {
475 } else if (ret == 0) {
480 if (FD_ISSET(svc_ctx->fd, &set)) {
481 client_fd = secure_socket_get_connection_handle(svc_ctx->fd);
487 tcb = tcb_create(svc_ctx, client_fd);
489 secure_socket_destroy_handle(client_fd);
492 if (FD_ISSET(svc_ctx->tcb_pipe[PIPE_READ], &set)) {
493 if (read(svc_ctx->tcb_pipe[PIPE_READ], &tcb, sizeof(tcb)) != sizeof(tcb)) {
500 * Invoke the service thread main, to notify the termination of a TCB
502 ret = svc_ctx->service_thread_main(tcb, NULL, svc_ctx->service_thread_data);
505 * at this time, the client thread can access this tcb.
506 * how can I protect this TCB from deletion without disturbing the server thread?
508 tcb_destroy(svc_ctx, tcb);
511 if (FD_ISSET(svc_ctx->evt_pipe[PIPE_READ], &set)) {
512 if (read(svc_ctx->evt_pipe[PIPE_READ], &evt_ch, sizeof(evt_ch)) != sizeof(evt_ch)) {
517 CRITICAL_SECTION_BEGIN(&svc_ctx->packet_list_lock);
518 packet_info = eina_list_nth(svc_ctx->packet_list, 0);
519 svc_ctx->packet_list = eina_list_remove(svc_ctx->packet_list, packet_info);
520 CRITICAL_SECTION_END(&svc_ctx->packet_list_lock);
524 * What happens if the client thread is terminated, so the packet_info->tcb is deleted
525 * while processing svc_ctx->service_thread_main?
527 ret = svc_ctx->service_thread_main(packet_info->tcb, packet_info->packet, svc_ctx->service_thread_data);
529 packet_destroy(packet_info->packet);
533 processing_timer_event(svc_ctx, &set);
534 /* If there is no such triggered FD? */
538 * Consuming all pended packets before terminates server thread.
540 * If the server thread is terminated, we should flush all pended packets.
541 * And we should services them.
542 * While processing this routine, the mutex is locked.
543 * So every other client thread will be slowed down, sequently, every clients can meet problems.
544 * But in case of termination of server thread, there could be systemetic problem.
545 * This only should be happenes while terminating the master daemon process.
547 CRITICAL_SECTION_BEGIN(&svc_ctx->packet_list_lock);
548 EINA_LIST_FREE(svc_ctx->packet_list, packet_info) {
549 ret = read(svc_ctx->evt_pipe[PIPE_READ], &evt_ch, sizeof(evt_ch));
550 ret = svc_ctx->service_thread_main(packet_info->tcb, packet_info->packet, svc_ctx->service_thread_data);
551 packet_destroy(packet_info->packet);
554 CRITICAL_SECTION_END(&svc_ctx->packet_list_lock);
556 tcb_teminate_all(svc_ctx);
564 struct service_context *service_common_create(const char *addr, int (*service_thread_main)(struct tcb *tcb, struct packet *packet, void *data), void *data)
567 struct service_context *svc_ctx;
569 if (!service_thread_main || !addr) {
573 svc_ctx = calloc(1, sizeof(*svc_ctx));
578 svc_ctx->fd = secure_socket_create_server(addr);
579 if (svc_ctx->fd < 0) {
584 svc_ctx->service_thread_main = service_thread_main;
585 svc_ctx->service_thread_data = data;
587 fcntl(svc_ctx->fd, F_SETFD, FD_CLOEXEC);
589 fcntl(svc_ctx->fd, F_SETFL, O_NONBLOCK);
591 if (pipe2(svc_ctx->evt_pipe, O_NONBLOCK | O_CLOEXEC) < 0) {
592 secure_socket_destroy_handle(svc_ctx->fd);
597 if (pipe2(svc_ctx->tcb_pipe, O_NONBLOCK | O_CLOEXEC) < 0) {
598 CLOSE_PIPE(svc_ctx->evt_pipe);
599 secure_socket_destroy_handle(svc_ctx->fd);
604 status = pthread_mutex_init(&svc_ctx->packet_list_lock, NULL);
606 CLOSE_PIPE(svc_ctx->evt_pipe);
607 CLOSE_PIPE(svc_ctx->tcb_pipe);
608 secure_socket_destroy_handle(svc_ctx->fd);
613 status = pthread_create(&svc_ctx->server_thid, NULL, server_main, svc_ctx);
615 status = pthread_mutex_destroy(&svc_ctx->packet_list_lock);
616 CLOSE_PIPE(svc_ctx->evt_pipe);
617 CLOSE_PIPE(svc_ctx->tcb_pipe);
618 secure_socket_destroy_handle(svc_ctx->fd);
630 int service_common_destroy(struct service_context *svc_ctx)
640 * Terminate server thread
642 secure_socket_destroy_handle(svc_ctx->fd);
644 status = pthread_join(svc_ctx->server_thid, &ret);
646 status = pthread_mutex_destroy(&svc_ctx->packet_list_lock);
648 CLOSE_PIPE(svc_ctx->evt_pipe);
649 CLOSE_PIPE(svc_ctx->tcb_pipe);
658 int tcb_fd(struct tcb *tcb)
670 int tcb_client_type(struct tcb *tcb)
682 int tcb_client_type_set(struct tcb *tcb, enum tcb_type type)
695 struct service_context *tcb_svc_ctx(struct tcb *tcb)
707 int service_common_unicast_packet(struct tcb *tcb, struct packet *packet)
712 return secure_socket_send(tcb->fd, (void *)packet_data(packet), packet_size(packet));
719 int service_common_multicast_packet(struct tcb *tcb, struct packet *packet, int type)
723 struct service_context *svc_ctx;
729 svc_ctx = tcb->svc_ctx;
731 EINA_LIST_FOREACH(svc_ctx->tcb_list, l, target) {
732 if (target == tcb || target->type != type) {
736 ret = secure_socket_send(target->fd, (void *)packet_data(packet), packet_size(packet));
745 struct service_event_item *service_common_add_timer(struct service_context *svc_ctx, double timer, int (*timer_cb)(struct service_context *svc_cx, void *data), void *data)
747 struct service_event_item *item;
748 struct itimerspec spec;
750 item = calloc(1, sizeof(*item));
755 item->type = SERVICE_EVENT_TIMER;
756 item->info.timer.fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK | TFD_CLOEXEC);
757 if (item->info.timer.fd < 0) {
762 spec.it_interval.tv_sec = (time_t)timer;
763 spec.it_interval.tv_nsec = (timer - spec.it_interval.tv_sec) * 1000000000;
764 spec.it_value.tv_sec = 0;
765 spec.it_value.tv_nsec = 0;
767 if (timerfd_settime(item->info.timer.fd, 0, &spec, NULL) < 0) {
768 close(item->info.timer.fd);
773 item->event_cb = timer_cb;
776 svc_ctx->event_list = eina_list_append(svc_ctx->event_list, item);
784 int service_common_del_timer(struct service_context *svc_ctx, struct service_event_item *item)
786 if (!eina_list_data_find(svc_ctx->event_list, item)) {
790 svc_ctx->event_list = eina_list_remove(svc_ctx->event_list, item);
792 close(item->info.timer.fd);