summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorSung-jae Park <nicesj.park@samsung.com>2013-05-15 10:39:49 +0900
committerSung-jae Park <nicesj.park@samsung.com>2013-05-15 11:15:49 +0900
commit091d1a58b29d011c70afd67065f93877910593f8 (patch)
tree14fb14149da390da5b0266d9a9bb64716edc0aac /src
parentd6efe2dfba7a14c72847b5926cdf9d99f68d16d1 (diff)
downloaddata-provider-master-091d1a58b29d011c70afd67065f93877910593f8.tar.gz
data-provider-master-091d1a58b29d011c70afd67065f93877910593f8.tar.bz2
data-provider-master-091d1a58b29d011c70afd67065f93877910593f8.zip
Patch 8/8 Update language change event handling code. Change the env value too. Region & Language is handles separately. So I separate them using VCONFKEY like app-core. Region & Lang. Patch 7/8 Care the language change notification. Patch 6/8 Handling the reply packet correctly. Patch 5/8 Define smack rule for vconf key. Patch 4/8 Don't terminate the provider even if it gets SIGTERM. Patch 3/8 Remove printf and update systemd service file. Patch 2/8 Update systemd service file. Patch 1/8 Implement the utility service. 1. Service routine updated. If a client is disconnected, service routine will be called with packet NULL. 2. Utility service is implemented. For generating the shortcut icon image, this utility service is prepared. Master just route the request & event for generating shortcut icon. Then the service daemon will be launched. and it will start generating icon image file. The image file is created in specified folder & name by client. So the client has to manage the created image file. 3. service_common_timer API is implemented. Using timerfd. Now the timer event can be handled in a separated thread. Change-Id: I9ce2546aea86c324ea9846d9dc673d6475b706c3
Diffstat (limited to 'src')
-rw-r--r--src/badge_service.c4
-rw-r--r--src/instance.c93
-rw-r--r--src/main.c7
-rw-r--r--src/notification_service.c4
-rw-r--r--src/server.c12
-rw-r--r--src/service_common.c178
-rw-r--r--src/setting.c73
-rw-r--r--src/shortcut_service.c4
-rw-r--r--src/util.c2
-rw-r--r--src/utility_service.c298
10 files changed, 661 insertions, 14 deletions
diff --git a/src/badge_service.c b/src/badge_service.c
index db3e090..9b39e94 100644
--- a/src/badge_service.c
+++ b/src/badge_service.c
@@ -269,6 +269,10 @@ static int service_thread_main(struct tcb *tcb, struct packet *packet, void *dat
};
DbgPrint("TCB: %p, Packet: %p\n", tcb, packet);
+ if (!packet) {
+ DbgPrint("TCB: %p is terminated\n", tcb);
+ return 0;
+ }
command = packet_command(packet);
if (!command) {
diff --git a/src/instance.c b/src/instance.c
index 32d18cc..1005fdc 100644
--- a/src/instance.c
+++ b/src/instance.c
@@ -83,6 +83,11 @@ struct event_item {
void *data;
};
+struct tag_item {
+ char *tag;
+ void *data;
+};
+
struct inst_info {
struct pkg_info *info;
@@ -144,6 +149,8 @@ struct inst_info {
Ecore_Timer *update_timer; /*!< Only used for secured livebox */
Eina_List *delete_event_list;
+
+ Eina_List *data_list;
};
#define CLIENT_SEND_EVENT(instance, packet) ((instance)->client ? client_rpc_async_request((instance)->client, (packet)) : client_broadcast((instance), (packet)))
@@ -595,6 +602,7 @@ static inline void destroy_instance(struct inst_info *inst)
enum pd_type pd_type;
struct slave_node *slave;
struct event_item *item;
+ struct tag_item *tag_item;
invoke_delete_callbacks(inst);
@@ -633,6 +641,12 @@ static inline void destroy_instance(struct inst_info *inst)
if (inst->update_timer)
ecore_timer_del(inst->update_timer);
+ EINA_LIST_FREE(inst->data_list, tag_item) {
+ DbgPrint("Tagged item[%s] %p\n", tag_item->tag, tag_item->data);
+ free(tag_item->tag);
+ free(tag_item);
+ }
+
/*!
* \note
*
@@ -1301,6 +1315,9 @@ static int pd_buffer_close_cb(struct client_node *client, void *inst)
ret = instance_slave_close_pd(inst, client);
DbgPrint("Forcely close the PD ret: %d\n", ret);
+
+ instance_unref(inst);
+
return -1; /* Delete this callback */
}
@@ -1315,6 +1332,8 @@ static int pd_script_close_cb(struct client_node *client, void *inst)
ret = instance_slave_close_pd(inst, client);
DbgPrint("Forcely close the PD ret: %d\n", ret);
+ instance_unref(inst);
+
return -1; /* Delete this callback */
}
@@ -3038,4 +3057,78 @@ HAPI void instance_fini(void)
{
}
+static inline struct tag_item *find_tag_item(struct inst_info *inst, const char *tag)
+{
+ struct tag_item *item;
+ Eina_List *l;
+
+ EINA_LIST_FOREACH(inst->data_list, l, item) {
+ if (!strcmp(item->tag, tag))
+ return item;
+ }
+
+ return NULL;
+}
+
+HAPI int instance_set_data(struct inst_info *inst, const char *tag, void *data)
+{
+ struct tag_item *item;
+
+ item = find_tag_item(inst, tag);
+ if (!item) {
+ item = malloc(sizeof(*item));
+ if (!item) {
+ ErrPrint("Heap: %s\n", strerror(errno));
+ return LB_STATUS_ERROR_MEMORY;
+ }
+
+ item->tag = strdup(tag);
+ if (!item->tag) {
+ ErrPrint("Heap: %s\n", strerror(errno));
+ free(item);
+ return LB_STATUS_ERROR_MEMORY;
+ }
+
+ inst->data_list = eina_list_append(inst->data_list, item);
+ }
+
+ if (!data) {
+ inst->data_list = eina_list_remove(inst->data_list, item);
+ free(item->tag);
+ free(item);
+ } else {
+ item->data = data;
+ }
+
+ return LB_STATUS_SUCCESS;
+}
+
+HAPI void *instance_del_data(struct inst_info *inst, const char *tag)
+{
+ struct tag_item *item;
+ void *data;
+
+ item = find_tag_item(inst, tag);
+ if (!item)
+ return NULL;
+
+ inst->data_list = eina_list_remove(inst->data_list, item);
+ data = item->data;
+ free(item->tag);
+ free(item);
+
+ return data;
+}
+
+HAPI void *instance_get_data(struct inst_info *inst, const char *tag)
+{
+ struct tag_item *item;
+
+ item = find_tag_item(inst, tag);
+ if (!item)
+ return NULL;
+
+ return item->data;
+}
+
/* End of a file */
diff --git a/src/main.c b/src/main.c
index 63a16e0..3816703 100644
--- a/src/main.c
+++ b/src/main.c
@@ -53,6 +53,7 @@
#include "event.h"
#include "shortcut_service.h"
#include "notification_service.h"
+#include "utility_service.h"
#include "badge_service.h"
#if defined(FLOG)
@@ -156,11 +157,11 @@ static inline int app_terminate(void)
static void signal_handler(int signum, siginfo_t *info, void *unused)
{
int fd;
+
CRITICAL_LOG("Terminated(SIGTERM)\n");
fd = creat("/tmp/.stop.provider", 0644);
if (fd > 0)
close(fd);
- exit(0);
}
int main(int argc, char *argv[])
@@ -174,7 +175,7 @@ int main(int argc, char *argv[])
*/
ret = critical_log_init(util_basename(argv[0]));
if (ret < 0)
- fprintf(stderr, "Failed to init the critical log\n");
+ ErrPrint("Failed to init the critical log\n");
#if defined(FLOG)
__file_log_fp = fopen("/tmp/live.log", "w+t");
@@ -245,6 +246,7 @@ int main(int argc, char *argv[])
shortcut_service_init();
notification_service_init();
badge_service_init();
+ utility_service_init();
script_init();
app_create();
@@ -256,6 +258,7 @@ int main(int argc, char *argv[])
app_terminate();
script_fini();
+ utility_service_fini();
badge_service_fini();
notification_service_fini();
shortcut_service_fini();
diff --git a/src/notification_service.c b/src/notification_service.c
index 2c2bcea..40f3b91 100644
--- a/src/notification_service.c
+++ b/src/notification_service.c
@@ -329,6 +329,10 @@ static int service_thread_main(struct tcb *tcb, struct packet *packet, void *dat
};
DbgPrint("TCB: %p, Packet: %p\n", tcb, packet);
+ if (!packet) {
+ DbgPrint("TCB: %p is terminated\n", tcb);
+ return 0;
+ }
command = packet_command(packet);
if (!command) {
diff --git a/src/server.c b/src/server.c
index fea14c9..d7ab848 100644
--- a/src/server.c
+++ b/src/server.c
@@ -5645,6 +5645,10 @@ out:
static Eina_Bool lazy_pd_created_cb(void *data)
{
+ /*!
+ * After unref instance first,
+ * if the instance is not destroyed, try to notify the created PD event to the client.
+ */
if (instance_unref(data)) {
int ret;
ret = instance_client_pd_created(data, LB_STATUS_SUCCESS);
@@ -5807,6 +5811,14 @@ static struct packet *client_create_pd(pid_t pid, int handle, const struct packe
* the event correctly.
*/
inst = instance_ref(inst); /* To guarantee the inst */
+
+ /*!
+ * \note
+ * At here, we don't need to rememeber the timer object.
+ * Even if the timer callback is called, after the instance is destroyed.
+ * lazy_pd_created_cb will decrease the instance refcnt first.
+ * At that time, if the instance is released, the timer callback will do nothing.
+ */
if (!ecore_timer_add(DELAY_TIME, lazy_pd_created_cb, inst)) {
instance_unref(inst);
script_handler_unload(instance_pd_script(inst), 1);
diff --git a/src/service_common.c b/src/service_common.c
index 6a32976..0190bf6 100644
--- a/src/service_common.c
+++ b/src/service_common.c
@@ -21,6 +21,7 @@
#include <errno.h>
#include <sys/time.h>
#include <sys/types.h>
+#include <sys/timerfd.h>
#include <unistd.h>
#include <fcntl.h>
@@ -37,11 +38,25 @@
int errno;
+struct service_event_item {
+ enum {
+ SERVICE_EVENT_TIMER,
+ } type;
+
+ union {
+ struct {
+ int fd;
+ } timer;
+ } info;
+
+ int (*event_cb)(struct service_context *svc_cx, void *data);
+ void *cbdata;
+};
+
/*!
* \note
* Server information and global (only in this file-scope) variables are defined
*/
-
struct service_context {
pthread_t server_thid; /*!< Server thread Id */
int fd; /*!< Server socket handle */
@@ -55,6 +70,8 @@ struct service_context {
int (*service_thread_main)(struct tcb *tcb, struct packet *packet, void *data);
void *service_thread_data;
+
+ Eina_List *event_list;
};
struct packet_info {
@@ -380,6 +397,88 @@ static inline void tcb_destroy(struct service_context *svc_ctx, struct tcb *tcb)
}
/*!
+ * \note
+ * SERVER THREAD
+ */
+static inline int find_max_fd(struct service_context *svc_ctx)
+{
+ int fd;
+ Eina_List *l;
+ struct service_event_item *item;
+
+ fd = svc_ctx->fd > svc_ctx->tcb_pipe[PIPE_READ] ? svc_ctx->fd : svc_ctx->tcb_pipe[PIPE_READ];
+ fd = fd > svc_ctx->evt_pipe[PIPE_READ] ? fd : svc_ctx->evt_pipe[PIPE_READ];
+
+ EINA_LIST_FOREACH(svc_ctx->event_list, l, item) {
+ if (item->type == SERVICE_EVENT_TIMER && fd < item->info.timer.fd)
+ fd = item->info.timer.fd;
+ }
+
+ fd += 1;
+ return fd;
+}
+
+/*!
+ * \note
+ * SERVER THREAD
+ */
+static inline void update_fdset(struct service_context *svc_ctx, fd_set *set)
+{
+ Eina_List *l;
+ struct service_event_item *item;
+
+ FD_ZERO(set);
+ FD_SET(svc_ctx->fd, set);
+ FD_SET(svc_ctx->tcb_pipe[PIPE_READ], set);
+ FD_SET(svc_ctx->evt_pipe[PIPE_READ], set);
+
+ EINA_LIST_FOREACH(svc_ctx->event_list, l, item) {
+ if (item->type == SERVICE_EVENT_TIMER)
+ FD_SET(item->info.timer.fd, set);
+ }
+}
+
+/*!
+ * \note
+ * SERVER THREAD
+ */
+static inline void processing_timer_event(struct service_context *svc_ctx, fd_set *set)
+{
+ uint64_t expired_count;
+ Eina_List *l;
+ Eina_List *n;
+ struct service_event_item *item;
+
+ EINA_LIST_FOREACH_SAFE(svc_ctx->event_list, l, n, item) {
+ switch (item->type) {
+ case SERVICE_EVENT_TIMER:
+ if (!FD_ISSET(item->info.timer.fd, set))
+ break;
+
+ if (read(item->info.timer.fd, &expired_count, sizeof(expired_count)) == sizeof(expired_count)) {
+ DbgPrint("Expired %d times\n", expired_count);
+ if (item->event_cb(svc_ctx, item->cbdata) >= 0)
+ break;
+ } else {
+ ErrPrint("read: %s\n", strerror(errno));
+ }
+
+ if (!eina_list_data_find(svc_ctx->event_list, item))
+ break;
+
+ svc_ctx->event_list = eina_list_remove(svc_ctx->event_list, item);
+ if (close(item->info.timer.fd) < 0)
+ ErrPrint("close: %s\n", strerror(errno));
+ free(item);
+ break;
+ default:
+ ErrPrint("Unknown event: %d\n", item->type);
+ break;
+ }
+ }
+}
+
+/*!
* Accept new client connections
* And create a new thread for service.
*
@@ -398,15 +497,9 @@ static void *server_main(void *data)
struct packet_info *packet_info;
DbgPrint("Server thread is activated\n");
- fd = svc_ctx->fd > svc_ctx->tcb_pipe[PIPE_READ] ? svc_ctx->fd : svc_ctx->tcb_pipe[PIPE_READ];
- fd = fd > svc_ctx->evt_pipe[PIPE_READ] ? fd : svc_ctx->evt_pipe[PIPE_READ];
- fd += 1;
-
while (1) {
- FD_ZERO(&set);
- FD_SET(svc_ctx->fd, &set);
- FD_SET(svc_ctx->tcb_pipe[PIPE_READ], &set);
- FD_SET(svc_ctx->evt_pipe[PIPE_READ], &set);
+ fd = find_max_fd(svc_ctx);
+ update_fdset(svc_ctx, &set);
ret = select(fd, &set, NULL, NULL, NULL);
if (ret < 0) {
@@ -446,6 +539,12 @@ static void *server_main(void *data)
break;
}
+ /*!
+ * \note
+ * Invoke the service thread main, to notify the termination of a TCB
+ */
+ ret = svc_ctx->service_thread_main(tcb, NULL, svc_ctx->service_thread_data);
+
DbgPrint("Destroying TCB[%p]\n", tcb);
/*!
* at this time, the client thread can access this tcb.
@@ -481,6 +580,7 @@ static void *server_main(void *data)
free(packet_info);
}
+ processing_timer_event(svc_ctx, &set);
/* If there is no such triggered FD? */
}
@@ -721,4 +821,64 @@ HAPI int service_common_multicast_packet(struct tcb *tcb, struct packet *packet,
return 0;
}
+/*!
+ * \note
+ * SERVER THREAD
+ */
+HAPI 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)
+{
+ struct service_event_item *item;
+ struct itimerspec spec;
+
+ item = calloc(1, sizeof(*item));
+ if (!item) {
+ ErrPrint("Heap: %s\n", strerror(errno));
+ return NULL;
+ }
+
+ item->type = SERVICE_EVENT_TIMER;
+ item->info.timer.fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK | TFD_CLOEXEC);
+ if (item->info.timer.fd < 0) {
+ ErrPrint("Error: %s\n", strerror(errno));
+ free(item);
+ return NULL;
+ }
+
+ spec.it_interval.tv_sec = (time_t)timer;
+ spec.it_interval.tv_nsec = (timer - spec.it_interval.tv_sec) * 1000000000;
+ spec.it_value.tv_sec = 0;
+ spec.it_value.tv_nsec = 0;
+
+ if (timerfd_settime(item->info.timer.fd, 0, &spec, NULL) < 0) {
+ ErrPrint("Error: %s\n", strerror(errno));
+ close(item->info.timer.fd);
+ free(item);
+ return NULL;
+ }
+
+ item->event_cb = timer_cb;
+ item->cbdata = data;
+
+ svc_ctx->event_list = eina_list_append(svc_ctx->event_list, item);
+ return item;
+}
+
+/*!
+ * \note
+ * SERVER THREAD
+ */
+HAPI int service_common_del_timer(struct service_context *svc_ctx, struct service_event_item *item)
+{
+ if (!eina_list_data_find(svc_ctx->event_list, item)) {
+ ErrPrint("Invalid event item\n");
+ return -EINVAL;
+ }
+
+ svc_ctx->event_list = eina_list_remove(svc_ctx->event_list, item);
+
+ close(item->info.timer.fd);
+ free(item);
+ return 0;
+}
+
/* End of a file */
diff --git a/src/setting.c b/src/setting.c
index 94b103a..5f6bdaf 100644
--- a/src/setting.c
+++ b/src/setting.c
@@ -22,6 +22,7 @@
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
+#include <locale.h>
#include <vconf.h>
#include <dlog.h>
@@ -80,24 +81,91 @@ static void power_off_cb(keynode_t *node, void *user_data)
}
}
+static void region_changed_cb(keynode_t *node, void *user_data)
+{
+ char *region;
+ char *r;
+
+ region = vconf_get_str(VCONFKEY_REGIONFORMAT);
+ if (!region)
+ return;
+
+ setenv("LC_CTYPE", region, 1);
+ setenv("LC_NUMERIC", region, 1);
+ setenv("LC_TIME", region, 1);
+ setenv("LC_COLLATE", region, 1);
+ setenv("LC_MONETARY", region, 1);
+ setenv("LC_PAPER", region, 1);
+ setenv("LC_NAME", region, 1);
+ setenv("LC_ADDRESS", region, 1);
+ setenv("LC_TELEPHONE", region, 1);
+ setenv("LC_MEASUREMENT", region, 1);
+ setenv("LC_IDENTIFICATION", region, 1);
+
+ r = setlocale(LC_ALL, "");
+ if (r == NULL)
+ ErrPrint("Failed to change region\n");
+
+ free(region);
+}
+
+static void lang_changed_cb(keynode_t *node, void *user_data)
+{
+ char *lang;
+ char *r;
+
+ lang = vconf_get_str(VCONFKEY_LANGSET);
+ if (!lang)
+ return;
+
+ setenv("LANG", lang, 1);
+ setenv("LC_MESSAGES", lang, 1);
+
+ r = setlocale(LC_ALL, "");
+ if (!r)
+ ErrPrint("Failed to change locale\n");
+
+ DbgPrint("Locale: %s\n", setlocale(LC_ALL, NULL));
+ free(lang);
+}
+
HAPI int setting_init(void)
{
int ret;
ret = vconf_notify_key_changed(VCONFKEY_PM_STATE, lcd_state_cb, NULL);
if (ret < 0)
- ErrPrint("Failed to add vconf for lock state\n");
+ ErrPrint("Failed to add vconf for lock state: %d\n", ret);
ret = vconf_notify_key_changed(VCONFKEY_SYSMAN_POWER_OFF_STATUS, power_off_cb, NULL);
if (ret < 0)
- ErrPrint("Failed to add vconf for power state\n");
+ ErrPrint("Failed to add vconf for power state: %d \n", ret);
+
+ ret = vconf_notify_key_changed(VCONFKEY_LANGSET, lang_changed_cb, NULL);
+ if (ret < 0)
+ ErrPrint("Failed to add vconf for lang change: %d\n", ret);
+ ret = vconf_notify_key_changed(VCONFKEY_REGIONFORMAT, region_changed_cb, NULL);
+ if (ret < 0)
+ ErrPrint("Failed to add vconf for region change: %d\n", ret);
+
+ lang_changed_cb(NULL, NULL);
+ region_changed_cb(NULL, NULL);
return ret;
}
HAPI int setting_fini(void)
{
int ret;
+
+ ret = vconf_ignore_key_changed(VCONFKEY_REGIONFORMAT, region_changed_cb);
+ if (ret < 0)
+ ErrPrint("Failed to ignore vconf key (%d)\n", ret);
+
+ ret = vconf_ignore_key_changed(VCONFKEY_LANGSET, lang_changed_cb);
+ if (ret < 0)
+ ErrPrint("Failed to ignore vconf key (%d)\n", ret);
+
ret = vconf_ignore_key_changed(VCONFKEY_PM_STATE, lcd_state_cb);
if (ret < 0)
ErrPrint("Failed to ignore vconf key (%d)\n", ret);
@@ -105,6 +173,7 @@ HAPI int setting_fini(void)
ret = vconf_ignore_key_changed(VCONFKEY_SYSMAN_POWER_OFF_STATUS, power_off_cb);
if (ret < 0)
ErrPrint("Failed to ignore vconf key (%d)\n", ret);
+
return ret;
}
diff --git a/src/shortcut_service.c b/src/shortcut_service.c
index 73e6999..81b9005 100644
--- a/src/shortcut_service.c
+++ b/src/shortcut_service.c
@@ -95,6 +95,10 @@ static int service_thread_main(struct tcb *tcb, struct packet *packet, void *dat
const char *command;
DbgPrint("TCB: %p, Packet: %p\n", tcb, packet);
+ if (!packet) {
+ DbgPrint("TCB: %p is terminated\n", tcb);
+ return 0;
+ }
command = packet_command(packet);
if (!command) {
diff --git a/src/util.c b/src/util.c
index a95eb37..5f0674c 100644
--- a/src/util.c
+++ b/src/util.c
@@ -249,7 +249,7 @@ HAPI char *util_replace_string(const char *src, const char *pattern, const char
out_sz = strlen(src);
ret = strdup(src);
if (!ret) {
- printf("Heap: %s\n", strerror(errno));
+ ErrPrint("Heap: %s\n", strerror(errno));
return NULL;
}
diff --git a/src/utility_service.c b/src/utility_service.c
new file mode 100644
index 0000000..0b7367d
--- /dev/null
+++ b/src/utility_service.c
@@ -0,0 +1,298 @@
+/*
+ * Copyright 2013 Samsung Electronics Co., Ltd
+ *
+ * Licensed under the Flora License, Version 1.1 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://floralicense.org/license/
+ *
+ * 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.
+ */
+#include <stdio.h>
+
+#include <Eina.h>
+
+#include <dlog.h>
+#include <aul.h>
+
+#include <livebox-errno.h>
+#include <packet.h>
+
+#include "service_common.h"
+#include "utility_service.h"
+#include "debug.h"
+#include "util.h"
+#include "conf.h"
+
+#define UTILITY_ADDR "/tmp/.utility.service"
+#define SVC_PKG "org.tizen.data-provider-slave.icon"
+#define LAUNCH_TIMEOUT 10.0f
+
+static struct info {
+ Eina_List *pending_list;
+ Eina_List *context_list;
+ struct service_context *svc_ctx;
+
+ struct tcb *svc_daemon;
+ pid_t svc_pid;
+
+ struct service_event_item *launch_timer;
+} s_info = {
+ .pending_list = NULL,
+ .context_list = NULL, /*!< \WARN: This is only used for SERVICE THREAD */
+ .svc_ctx = NULL, /*!< \WARN: This is only used for MAIN THREAD */
+
+ .svc_daemon = NULL,
+ .svc_pid = -1,
+
+ .launch_timer = NULL,
+};
+
+struct pending_item {
+ struct tcb *tcb;
+ struct packet *packet;
+};
+
+struct context {
+ struct tcb *tcb;
+ double seq;
+};
+
+static inline int put_reply_tcb(struct tcb *tcb, double seq)
+{
+ struct context *ctx;
+
+ ctx = malloc(sizeof(*ctx));
+ if (!ctx) {
+ ErrPrint("Heap: %s\n", strerror(errno));
+ return LB_STATUS_ERROR_MEMORY;
+ }
+
+ ctx->tcb = tcb;
+ ctx->seq = seq;
+
+ s_info.context_list = eina_list_append(s_info.context_list, ctx);
+ return LB_STATUS_SUCCESS;
+}
+
+static inline struct tcb *get_reply_tcb(double seq)
+{
+ Eina_List *l;
+ Eina_List *n;
+ struct context *ctx;
+ struct tcb *tcb;
+
+ EINA_LIST_FOREACH_SAFE(s_info.context_list, l, n, ctx) {
+ if (ctx->seq != seq)
+ continue;
+
+ s_info.context_list = eina_list_remove(s_info.context_list, ctx);
+ tcb = ctx->tcb;
+ free(ctx);
+ return tcb;
+ }
+
+ return NULL;
+}
+
+static inline int flush_pended_request(void)
+{
+ /*!
+ * Flush all pended requests
+ */
+ struct pending_item *item;
+ int ret;
+
+ EINA_LIST_FREE(s_info.pending_list, item) {
+ ret = service_common_unicast_packet(s_info.svc_daemon, item->packet);
+ if (ret < 0) {
+ struct packet *reply;
+ reply = packet_create_reply(item->packet, "i", ret);
+ if (service_common_unicast_packet(item->tcb, reply) < 0)
+ ErrPrint("Unable to send packet\n");
+ packet_destroy(reply);
+ } else {
+ put_reply_tcb(item->tcb, packet_seq(item->packet));
+ }
+ packet_unref(item->packet);
+ free(item);
+ }
+
+ return 0;
+}
+
+static inline int put_pended_request(struct tcb *tcb, struct packet *packet)
+{
+ struct pending_item *item;
+
+ item = malloc(sizeof(*item));
+ if (!item) {
+ ErrPrint("Heap: %s\n", strerror(errno));
+ return LB_STATUS_ERROR_MEMORY;
+ }
+
+ item->tcb = tcb;
+ item->packet = packet_ref(packet);
+ if (!item->packet) {
+ free(item);
+ ErrPrint("Unable to ref packet\n");
+ return LB_STATUS_ERROR_FAULT;
+ }
+
+ s_info.pending_list = eina_list_append(s_info.pending_list, item);
+ return 0;
+}
+
+static int launch_timeout_cb(struct service_context *svc_ctx, void *data)
+{
+ struct pending_item *item;
+ struct packet *reply;
+
+ EINA_LIST_FREE(s_info.pending_list, item) {
+ reply = packet_create_reply(item->packet, "i", -EFAULT);
+ if (!reply) {
+ ErrPrint("Unable to create a packet\n");
+ } else {
+ int ret;
+
+ ret = service_common_unicast_packet(item->tcb, reply);
+ if (ret < 0)
+ ErrPrint("Failed to send reply packet: %d\n", ret);
+
+ packet_destroy(reply);
+ }
+
+ packet_unref(item->packet);
+ free(item);
+ }
+
+ s_info.launch_timer = NULL;
+ return -ECANCELED; /* Delete this timer */
+}
+
+static int service_thread_main(struct tcb *tcb, struct packet *packet, void *data)
+{
+ struct packet *reply;
+ const char *cmd;
+ int ret;
+
+ if (!packet) {
+ DbgPrint("TCB %p is terminated\n", tcb);
+
+ if (tcb == s_info.svc_daemon) {
+ s_info.svc_daemon = NULL;
+ s_info.svc_pid = -1;
+ }
+
+ return 0;
+ }
+
+ cmd = packet_command(packet);
+ if (!cmd) {
+ ErrPrint("Invalid packet\n");
+ return LB_STATUS_ERROR_INVALID;
+ }
+
+ switch (packet_type(packet)) {
+ case PACKET_REQ:
+ if (s_info.svc_pid < 0) {
+ s_info.svc_pid = aul_launch_app(SVC_PKG, NULL);
+ if (s_info.svc_pid > 0) {
+ s_info.launch_timer = service_common_add_timer(tcb_svc_ctx(tcb), LAUNCH_TIMEOUT, launch_timeout_cb, NULL);
+ if (!s_info.launch_timer)
+ ErrPrint("Unable to create launch timer\n");
+ }
+ }
+
+ if (s_info.svc_pid < 0) {
+ ErrPrint("Failed to launch an app: %s(%d)\n", SVC_PKG, s_info.svc_pid);
+ ret = LB_STATUS_ERROR_FAULT;
+ goto reply_out;
+ } else if (!s_info.svc_daemon) {
+ ret = put_pended_request(tcb, packet);
+ if (ret < 0)
+ goto reply_out;
+ } else {
+ ret = service_common_unicast_packet(s_info.svc_daemon, packet);
+ if (ret <0)
+ goto reply_out;
+
+ put_reply_tcb(tcb, packet_seq(packet));
+ }
+
+ break;
+ case PACKET_REQ_NOACK:
+ if (!strcmp(cmd, "service_register")) {
+ if (s_info.svc_daemon) {
+ ErrPrint("Service daemon is already prepared\n");
+ return LB_STATUS_ERROR_INVALID;
+ }
+
+ if (s_info.launch_timer) {
+ service_common_del_timer(tcb_svc_ctx(tcb), s_info.launch_timer);
+ s_info.launch_timer = NULL;
+ }
+
+ s_info.svc_daemon = tcb;
+ flush_pended_request();
+ }
+ break;
+ case PACKET_ACK:
+ tcb = get_reply_tcb(packet_seq(packet));
+ if (!tcb) {
+ ErrPrint("Unable to find reply tcb\n");
+ } else {
+ ret = service_common_unicast_packet(tcb, packet);
+ if (ret < 0)
+ ErrPrint("Unable to forward the reply packet\n");
+ }
+ break;
+ default:
+ ErrPrint("Packet type is not valid[%s]\n", cmd);
+ return LB_STATUS_ERROR_INVALID;
+ }
+
+ return LB_STATUS_SUCCESS;
+
+reply_out:
+ ErrPrint("Error: %d\n", ret);
+ reply = packet_create_reply(packet, "i", ret);
+ if (service_common_unicast_packet(tcb, reply) < 0)
+ ErrPrint("Unable to send reply packet\n");
+ packet_destroy(reply);
+ return ret;
+}
+
+int utility_service_init(void)
+{
+ if (s_info.svc_ctx) {
+ ErrPrint("Already initialized\n");
+ return LB_STATUS_ERROR_ALREADY;
+ }
+
+ s_info.svc_ctx = service_common_create(UTILITY_ADDR, service_thread_main, NULL);
+ if (!s_info.svc_ctx) {
+ ErrPrint("Unable to activate service thread\n");
+ return LB_STATUS_ERROR_FAULT;
+ }
+
+ DbgPrint("Successfully initiated\n");
+ return LB_STATUS_SUCCESS;
+}
+
+int utility_service_fini(void)
+{
+ if (!s_info.svc_ctx)
+ return LB_STATUS_ERROR_INVALID;
+
+ service_common_destroy(s_info.svc_ctx);
+ DbgPrint("Successfully Finalized\n");
+ return LB_STATUS_SUCCESS;
+}
+
+/* End of a file */