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: I2873c9232f9f396c6b8ad3345fa041939cb4aa7c
src/shortcut_service.c
src/badge_service.c
src/notification_service.c
+ src/utility_service.c
src/service_common.c
)
</provide>
<request>
<smack request="sys-assert::core" type="rwxat" />
+ <!--
+ <smack request="notification::db" type="rw" />
+ <smack request="badge::db" type="rw" />
+ -->
</request>
<permit>
<smack permit="dbus" type="rx" />
- <smack permit="notification::db" type="rw" />
- <smack permit="badge::db" type="rw" />
</permit>
</define>
<request>
extern int instance_event_callback_add(struct inst_info *inst, enum instance_event type, int (*event_cb)(struct inst_info *inst, void *data), void *data);
extern int instance_event_callback_del(struct inst_info *inst, enum instance_event type, int (*event_cb)(struct inst_info *inst, void *data));
+/*!
+ */
+extern int instance_set_data(struct inst_info *inst, const char *tag, void *data);
+extern void *instance_del_data(struct inst_info *inst, const char *tag);
+extern void *instance_get_data(struct inst_info *inst, const char *tag);
+
/* End of a file */
struct tcb;
struct service_context;
+struct service_event_item;
extern int tcb_fd(struct tcb *tcb);
extern struct service_context *tcb_svc_ctx(struct tcb *tcb);
extern int service_common_multicast_packet(struct tcb *tcb, struct packet *packet, int type);
extern int service_common_unicast_packet(struct tcb *tcb, struct packet *packet);
+extern 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);
+extern int service_common_del_timer(struct service_context *svc_ctx, struct service_event_item *item);
+
/* End of a file */
--- /dev/null
+/*
+ * 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.
+ */
+
+extern int utility_service_init(void);
+extern int utility_service_fini(void);
+
+/* End of a file */
+
};
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) {
void *data;
};
+struct tag_item {
+ char *tag;
+ void *data;
+};
+
struct inst_info {
struct pkg_info *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)))
enum pd_type pd_type;
struct slave_node *slave;
struct event_item *item;
+ struct tag_item *tag_item;
invoke_delete_callbacks(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
*
ret = instance_slave_close_pd(inst, client);
DbgPrint("Forcely close the PD ret: %d\n", ret);
+
+ instance_unref(inst);
+
return -1; /* Delete this callback */
}
ret = instance_slave_close_pd(inst, client);
DbgPrint("Forcely close the PD ret: %d\n", ret);
+ instance_unref(inst);
+
return -1; /* Delete this callback */
}
{
}
+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 */
#include "event.h"
#include "shortcut_service.h"
#include "notification_service.h"
+#include "utility_service.h"
#include "badge_service.h"
#if defined(FLOG)
shortcut_service_init();
notification_service_init();
badge_service_init();
+ utility_service_init();
script_init();
app_create();
app_terminate();
script_fini();
+ utility_service_fini();
badge_service_fini();
notification_service_fini();
shortcut_service_fini();
};
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) {
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);
* 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);
#include <errno.h>
#include <sys/time.h>
#include <sys/types.h>
+#include <sys/timerfd.h>
#include <unistd.h>
#include <fcntl.h>
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 */
int (*service_thread_main)(struct tcb *tcb, struct packet *packet, void *data);
void *service_thread_data;
+
+ Eina_List *event_list;
};
struct packet_info {
}
/*!
+ * \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.
*
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) {
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.
free(packet_info);
}
+ processing_timer_event(svc_ctx, &set);
/* If there is no such triggered FD? */
}
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 */
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) {
--- /dev/null
+/*
+ * 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 "com.samsung.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);
+ }
+ 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 */