2 * Copyright (c) 2000 - 2013 Samsung Electronics Co., Ltd. All rights reserved.
4 * Licensed under the Apache License, Version 2.0 (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://www.apache.org/licenses/LICENSE-2.0
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.
20 #include <secure_socket.h>
24 #include <sys/types.h>
25 #include <sys/timerfd.h>
30 #include "service_common.h"
33 #define EVT_END_CH 'x'
37 struct service_event_item {
48 int (*event_cb)(struct service_context *svc_cx, void *data);
54 * Server information and global (only in this file-scope) variables are defined
56 struct service_context {
57 pthread_t server_thid; /*!< Server thread Id */
58 int fd; /*!< Server socket handle */
60 Eina_List *tcb_list; /*!< TCB list, list of every thread for client connections */
62 Eina_List *packet_list;
63 pthread_mutex_t packet_list_lock;
64 int evt_pipe[PIPE_MAX];
65 int tcb_pipe[PIPE_MAX];
67 int (*service_thread_main)(struct tcb *tcb, struct packet *packet, void *data);
68 void *service_thread_data;
70 Eina_List *event_list;
75 struct packet *packet;
80 * Thread Control Block
81 * - The main server will create a thread for every client connections.
82 * When a new client is comming to us, this TCB block will be allocated and initialized.
84 struct tcb { /* Thread controll block */
85 struct service_context *svc_ctx;
86 pthread_t thid; /*!< Thread Id */
87 int fd; /*!< Connection handle */
92 * Do services for clients
93 * Routing packets to destination processes.
96 static void *client_packet_pump_main(void *data)
98 struct tcb *tcb = data;
99 struct service_context *svc_ctx = tcb->svc_ctx;
100 struct packet *packet;
108 char evt_ch = EVT_CH;
115 struct packet_info *packet_info;
119 recv_state = RECV_INIT;
122 * To escape from the switch statement, we use this ret value
126 FD_SET(tcb->fd, &set);
127 ret = select(tcb->fd + 1, &set, NULL, NULL, NULL);
130 if (errno == EINTR) {
137 } else if (ret == 0) {
144 if (!FD_ISSET(tcb->fd, &set)) {
153 * Service!!! Receive packet & route packet
155 switch (recv_state) {
157 size = packet_header_size();
166 recv_state = RECV_HEADER;
167 /* Go through, don't break from here */
169 ret = secure_socket_recv(tcb->fd, ptr, size - recv_offset, &pid);
181 if (recv_offset == size) {
182 packet = packet_build(packet, packet_offset, ptr, size);
190 packet_offset += recv_offset;
192 size = packet_payload_size(packet);
194 recv_state = RECV_DONE;
199 recv_state = RECV_PAYLOAD;
209 ret = secure_socket_recv(tcb->fd, ptr, size - recv_offset, &pid);
221 if (recv_offset == size) {
222 packet = packet_build(packet, packet_offset, ptr, size);
230 packet_offset += recv_offset;
232 recv_state = RECV_DONE;
242 if (recv_state == RECV_DONE) {
244 * Push this packet to the packet list with TCB
245 * Then the service main function will get this.
247 packet_info = malloc(sizeof(*packet_info));
250 packet_destroy(packet);
254 packet_info->packet = packet;
255 packet_info->tcb = tcb;
257 CRITICAL_SECTION_BEGIN(&svc_ctx->packet_list_lock);
258 svc_ctx->packet_list = eina_list_append(svc_ctx->packet_list, packet_info);
259 CRITICAL_SECTION_END(&svc_ctx->packet_list_lock);
261 if (write(svc_ctx->evt_pipe[PIPE_WRITE], &evt_ch, sizeof(evt_ch)) != sizeof(evt_ch)) {
263 CRITICAL_SECTION_BEGIN(&svc_ctx->packet_list_lock);
264 svc_ctx->packet_list = eina_list_remove(svc_ctx->packet_list, packet_info);
265 CRITICAL_SECTION_END(&svc_ctx->packet_list_lock);
267 packet_destroy(packet);
271 recv_state = RECV_INIT;
276 CRITICAL_SECTION_BEGIN(&svc_ctx->packet_list_lock);
277 EINA_LIST_FOREACH(svc_ctx->packet_list, l, packet_info) {
278 if (packet_info->tcb == tcb) {
279 packet_info->tcb = NULL;
282 CRITICAL_SECTION_END(&svc_ctx->packet_list_lock);
286 * Emit a signal to collect this TCB from the SERVER THREAD.
288 write(svc_ctx->tcb_pipe[PIPE_WRITE], &tcb, sizeof(tcb)) != sizeof(tcb);
297 static inline struct tcb *tcb_create(struct service_context *svc_ctx, int fd)
302 tcb = malloc(sizeof(*tcb));
308 tcb->svc_ctx = svc_ctx;
309 tcb->type = TCB_CLIENT_TYPE_APP;
311 status = pthread_create(&tcb->thid, NULL, client_packet_pump_main, tcb);
317 svc_ctx->tcb_list = eina_list_append(svc_ctx->tcb_list, tcb);
325 static inline void tcb_teminate_all(struct service_context *svc_ctx)
332 * We don't need to make critical section on here.
333 * If we call this after terminate the server thread first.
334 * Then there is no other thread to access tcb_list.
336 EINA_LIST_FREE(svc_ctx->tcb_list, tcb) {
338 * ASSERT(tcb->fd >= 0);
340 secure_socket_destroy_handle(tcb->fd);
342 status = pthread_join(tcb->thid, &ret);
352 static inline void tcb_destroy(struct service_context *svc_ctx, struct tcb *tcb)
357 svc_ctx->tcb_list = eina_list_remove(svc_ctx->tcb_list, tcb);
359 * ASSERT(tcb->fd >= 0);
360 * Close the connection, and then collecting the return value of thread
362 secure_socket_destroy_handle(tcb->fd);
364 status = pthread_join(tcb->thid, &ret);
373 static inline int find_max_fd(struct service_context *svc_ctx)
377 struct service_event_item *item;
379 fd = svc_ctx->fd > svc_ctx->tcb_pipe[PIPE_READ] ? svc_ctx->fd : svc_ctx->tcb_pipe[PIPE_READ];
380 fd = fd > svc_ctx->evt_pipe[PIPE_READ] ? fd : svc_ctx->evt_pipe[PIPE_READ];
382 EINA_LIST_FOREACH(svc_ctx->event_list, l, item) {
383 if (item->type == SERVICE_EVENT_TIMER && fd < item->info.timer.fd)
384 fd = item->info.timer.fd;
395 static inline void update_fdset(struct service_context *svc_ctx, fd_set *set)
398 struct service_event_item *item;
401 FD_SET(svc_ctx->fd, set);
402 FD_SET(svc_ctx->tcb_pipe[PIPE_READ], set);
403 FD_SET(svc_ctx->evt_pipe[PIPE_READ], set);
405 EINA_LIST_FOREACH(svc_ctx->event_list, l, item) {
406 if (item->type == SERVICE_EVENT_TIMER)
407 FD_SET(item->info.timer.fd, set);
415 static inline void processing_timer_event(struct service_context *svc_ctx, fd_set *set)
417 uint64_t expired_count;
420 struct service_event_item *item;
422 EINA_LIST_FOREACH_SAFE(svc_ctx->event_list, l, n, item) {
423 switch (item->type) {
424 case SERVICE_EVENT_TIMER:
425 if (!FD_ISSET(item->info.timer.fd, set))
428 if (read(item->info.timer.fd, &expired_count, sizeof(expired_count)) == sizeof(expired_count)) {
429 if (item->event_cb(svc_ctx, item->cbdata) >= 0)
433 if (!eina_list_data_find(svc_ctx->event_list, item))
436 svc_ctx->event_list = eina_list_remove(svc_ctx->event_list, item);
437 close(item->info.timer.fd);
448 * Accept new client connections
449 * And create a new thread for service.
451 * Create Client threads & Destroying them
454 static void *server_main(void *data)
456 struct service_context *svc_ctx = data;
463 struct packet_info *packet_info;
466 fd = find_max_fd(svc_ctx);
467 update_fdset(svc_ctx, &set);
469 ret = select(fd, &set, NULL, NULL, NULL);
472 if (errno == EINTR) {
476 } else if (ret == 0) {
481 if (FD_ISSET(svc_ctx->fd, &set)) {
482 client_fd = secure_socket_get_connection_handle(svc_ctx->fd);
488 tcb = tcb_create(svc_ctx, client_fd);
490 secure_socket_destroy_handle(client_fd);
493 if (FD_ISSET(svc_ctx->tcb_pipe[PIPE_READ], &set)) {
494 if (read(svc_ctx->tcb_pipe[PIPE_READ], &tcb, sizeof(tcb)) != sizeof(tcb)) {
501 * Invoke the service thread main, to notify the termination of a TCB
503 ret = svc_ctx->service_thread_main(tcb, NULL, svc_ctx->service_thread_data);
506 * at this time, the client thread can access this tcb.
507 * how can I protect this TCB from deletion without disturbing the server thread?
509 tcb_destroy(svc_ctx, tcb);
512 if (FD_ISSET(svc_ctx->evt_pipe[PIPE_READ], &set)) {
513 if (read(svc_ctx->evt_pipe[PIPE_READ], &evt_ch, sizeof(evt_ch)) != sizeof(evt_ch)) {
518 CRITICAL_SECTION_BEGIN(&svc_ctx->packet_list_lock);
519 packet_info = eina_list_nth(svc_ctx->packet_list, 0);
520 svc_ctx->packet_list = eina_list_remove(svc_ctx->packet_list, packet_info);
521 CRITICAL_SECTION_END(&svc_ctx->packet_list_lock);
525 * What happens if the client thread is terminated, so the packet_info->tcb is deleted
526 * while processing svc_ctx->service_thread_main?
528 ret = svc_ctx->service_thread_main(packet_info->tcb, packet_info->packet, svc_ctx->service_thread_data);
530 packet_destroy(packet_info->packet);
534 processing_timer_event(svc_ctx, &set);
535 /* If there is no such triggered FD? */
539 * Consuming all pended packets before terminates server thread.
541 * If the server thread is terminated, we should flush all pended packets.
542 * And we should services them.
543 * While processing this routine, the mutex is locked.
544 * So every other client thread will be slowed down, sequently, every clients can meet problems.
545 * But in case of termination of server thread, there could be systemetic problem.
546 * This only should be happenes while terminating the master daemon process.
548 CRITICAL_SECTION_BEGIN(&svc_ctx->packet_list_lock);
549 EINA_LIST_FREE(svc_ctx->packet_list, packet_info) {
550 ret = read(svc_ctx->evt_pipe[PIPE_READ], &evt_ch, sizeof(evt_ch));
551 ret = svc_ctx->service_thread_main(packet_info->tcb, packet_info->packet, svc_ctx->service_thread_data);
552 packet_destroy(packet_info->packet);
555 CRITICAL_SECTION_END(&svc_ctx->packet_list_lock);
557 tcb_teminate_all(svc_ctx);
565 struct service_context *service_common_create(const char *addr, int (*service_thread_main)(struct tcb *tcb, struct packet *packet, void *data), void *data)
568 struct service_context *svc_ctx;
570 if (!service_thread_main || !addr) {
574 svc_ctx = calloc(1, sizeof(*svc_ctx));
579 svc_ctx->fd = secure_socket_create_server(addr);
580 if (svc_ctx->fd < 0) {
585 svc_ctx->service_thread_main = service_thread_main;
586 svc_ctx->service_thread_data = data;
588 fcntl(svc_ctx->fd, F_SETFD, FD_CLOEXEC);
590 fcntl(svc_ctx->fd, F_SETFL, O_NONBLOCK);
592 if (pipe2(svc_ctx->evt_pipe, O_NONBLOCK | O_CLOEXEC) < 0) {
593 secure_socket_destroy_handle(svc_ctx->fd);
598 if (pipe2(svc_ctx->tcb_pipe, O_NONBLOCK | O_CLOEXEC) < 0) {
599 CLOSE_PIPE(svc_ctx->evt_pipe);
600 secure_socket_destroy_handle(svc_ctx->fd);
605 status = pthread_mutex_init(&svc_ctx->packet_list_lock, NULL);
607 CLOSE_PIPE(svc_ctx->evt_pipe);
608 CLOSE_PIPE(svc_ctx->tcb_pipe);
609 secure_socket_destroy_handle(svc_ctx->fd);
614 status = pthread_create(&svc_ctx->server_thid, NULL, server_main, svc_ctx);
616 status = pthread_mutex_destroy(&svc_ctx->packet_list_lock);
617 CLOSE_PIPE(svc_ctx->evt_pipe);
618 CLOSE_PIPE(svc_ctx->tcb_pipe);
619 secure_socket_destroy_handle(svc_ctx->fd);
631 int service_common_destroy(struct service_context *svc_ctx)
641 * Terminate server thread
643 secure_socket_destroy_handle(svc_ctx->fd);
645 status = pthread_join(svc_ctx->server_thid, &ret);
647 status = pthread_mutex_destroy(&svc_ctx->packet_list_lock);
649 CLOSE_PIPE(svc_ctx->evt_pipe);
650 CLOSE_PIPE(svc_ctx->tcb_pipe);
659 int tcb_fd(struct tcb *tcb)
671 int tcb_client_type(struct tcb *tcb)
683 int tcb_client_type_set(struct tcb *tcb, enum tcb_type type)
696 struct service_context *tcb_svc_ctx(struct tcb *tcb)
708 int service_common_unicast_packet(struct tcb *tcb, struct packet *packet)
713 return secure_socket_send(tcb->fd, (void *)packet_data(packet), packet_size(packet));
720 int service_common_multicast_packet(struct tcb *tcb, struct packet *packet, int type)
724 struct service_context *svc_ctx;
730 svc_ctx = tcb->svc_ctx;
732 EINA_LIST_FOREACH(svc_ctx->tcb_list, l, target) {
733 if (target == tcb || target->type != type) {
737 ret = secure_socket_send(target->fd, (void *)packet_data(packet), packet_size(packet));
746 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)
748 struct service_event_item *item;
749 struct itimerspec spec;
751 item = calloc(1, sizeof(*item));
756 item->type = SERVICE_EVENT_TIMER;
757 item->info.timer.fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK | TFD_CLOEXEC);
758 if (item->info.timer.fd < 0) {
763 spec.it_interval.tv_sec = (time_t)timer;
764 spec.it_interval.tv_nsec = (timer - spec.it_interval.tv_sec) * 1000000000;
765 spec.it_value.tv_sec = 0;
766 spec.it_value.tv_nsec = 0;
768 if (timerfd_settime(item->info.timer.fd, 0, &spec, NULL) < 0) {
769 close(item->info.timer.fd);
774 item->event_cb = timer_cb;
777 svc_ctx->event_list = eina_list_append(svc_ctx->event_list, item);
785 int service_common_del_timer(struct service_context *svc_ctx, struct service_event_item *item)
787 if (!eina_list_data_find(svc_ctx->event_list, item)) {
791 svc_ctx->event_list = eina_list_remove(svc_ctx->event_list, item);
793 close(item->info.timer.fd);