summaryrefslogtreecommitdiff
path: root/src/slave_life.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/slave_life.c')
-rw-r--r--src/slave_life.c1717
1 files changed, 1717 insertions, 0 deletions
diff --git a/src/slave_life.c b/src/slave_life.c
new file mode 100644
index 0000000..eeff1b3
--- /dev/null
+++ b/src/slave_life.c
@@ -0,0 +1,1717 @@
+/*
+ * 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 <string.h> /* strerror */
+#include <errno.h> /* errno */
+#include <unistd.h> /* pid_t */
+#include <stdlib.h> /* free */
+#include <pthread.h>
+#include <malloc.h>
+#include <sys/time.h>
+
+#include <Eina.h>
+#include <Ecore.h>
+
+#include <aul.h> /* aul_launch_app */
+#include <dlog.h>
+#include <bundle.h>
+
+#include <packet.h>
+#include <livebox-errno.h>
+
+#include "critical_log.h"
+#include "slave_life.h"
+#include "slave_rpc.h"
+#include "client_life.h"
+#include "fault_manager.h"
+#include "debug.h"
+#include "conf.h"
+#include "setting.h"
+#include "util.h"
+#include "abi.h"
+#include "xmonitor.h"
+
+int errno;
+
+struct slave_node {
+ char *name;
+ char *abi;
+ char *pkgname;
+ int secured; /* Only A package(livebox) is loaded for security requirements */
+ int refcnt;
+ int fault_count;
+ int critical_fault_count;
+ enum slave_state state;
+ int network;
+
+ int loaded_instance;
+ int loaded_package;
+
+ int reactivate_instances;
+ int reactivate_slave;
+
+ pid_t pid;
+
+ enum event_process {
+ SLAVE_EVENT_PROCESS_IDLE = 0x00,
+ SLAVE_EVENT_PROCESS_ACTIVATE = 0x01,
+ SLAVE_EVENT_PROCESS_DEACTIVATE = 0x02,
+ SLAVE_EVENT_PROCESS_DELETE = 0x04,
+ SLAVE_EVENT_PROCESS_FAULT = 0x08,
+ SLAVE_EVENT_PROCESS_PAUSE = 0x10,
+ SLAVE_EVENT_PROCESS_RESUME = 0x20
+ } in_event_process;
+ Eina_List *event_activate_list;
+ Eina_List *event_deactivate_list;
+ Eina_List *event_delete_list;
+ Eina_List *event_fault_list;
+ Eina_List *event_pause_list;
+ Eina_List *event_resume_list;
+
+ Eina_List *data_list;
+
+ Ecore_Timer *ttl_timer; /*!< Time to live */
+ Ecore_Timer *activate_timer; /*!< Waiting hello packet for this time */
+ Ecore_Timer *relaunch_timer; /*!< Try to relaunch service app */
+ Ecore_Timer *terminate_timer; /*!< Waiting this timer before terminate the service provider */
+ int relaunch_count;
+
+ int ctrl_option;
+
+#if defined(_USE_ECORE_TIME_GET)
+ double activated_at;
+#else
+ struct timeval activated_at;
+#endif
+};
+
+struct event {
+ struct slave_node *slave;
+
+ int (*evt_cb)(struct slave_node *, void *);
+ void *cbdata;
+ int deleted;
+};
+
+struct priv_data {
+ char *tag;
+ void *data;
+};
+
+static struct {
+ Eina_List *slave_list;
+ int deactivate_all_refcnt;
+} s_info = {
+ .slave_list = NULL,
+ .deactivate_all_refcnt = 0,
+};
+
+static Eina_Bool slave_ttl_cb(void *data)
+{
+ struct slave_node *slave = (struct slave_node *)data;
+
+ /*!
+ * \note
+ * ttl_timer must has to be set to NULL before deactivate the slave
+ * It will be used for making decision of the expired TTL timer or the fault of a livebox.
+ */
+ slave->ttl_timer = NULL;
+
+ slave_set_reactivation(slave, 0);
+ slave_set_reactivate_instances(slave, 1);
+
+ slave = slave_deactivate(slave, 1);
+ if (!slave) {
+ DbgPrint("Slave is deleted\n");
+ }
+
+ /*! To recover all instances state it is activated again */
+ return ECORE_CALLBACK_CANCEL;
+}
+
+static inline int xmonitor_pause_cb(void *data)
+{
+ slave_pause(data);
+ return LB_STATUS_SUCCESS;
+}
+
+static inline int xmonitor_resume_cb(void *data)
+{
+ slave_resume(data);
+ return LB_STATUS_SUCCESS;
+}
+
+static inline struct slave_node *create_slave_node(const char *name, int is_secured, const char *abi, const char *pkgname, int network)
+{
+ struct slave_node *slave;
+
+ slave = calloc(1, sizeof(*slave));
+ if (!slave) {
+ ErrPrint("Heap: %s\n", strerror(errno));
+ return NULL;
+ }
+
+ slave->name = strdup(name);
+ if (!slave->name) {
+ ErrPrint("Heap: %s\n", strerror(errno));
+ DbgFree(slave);
+ return NULL;
+ }
+
+ slave->abi = strdup(abi);
+ if (!slave->abi) {
+ ErrPrint("Heap: %s\n", strerror(errno));
+ DbgFree(slave->name);
+ DbgFree(slave);
+ return NULL;
+ }
+
+ slave->pkgname = strdup(pkgname);
+ if (!slave->pkgname) {
+ ErrPrint("Heap: %s\n", strerror(errno));
+ DbgFree(slave->abi);
+ DbgFree(slave->name);
+ DbgFree(slave);
+ return NULL;
+ }
+
+ slave->secured = is_secured;
+ slave->pid = (pid_t)-1;
+ slave->state = SLAVE_TERMINATED;
+ slave->network = network;
+ slave->relaunch_count = SLAVE_RELAUNCH_COUNT;
+
+ xmonitor_add_event_callback(XMONITOR_PAUSED, xmonitor_pause_cb, slave);
+ xmonitor_add_event_callback(XMONITOR_RESUMED, xmonitor_resume_cb, slave);
+
+ s_info.slave_list = eina_list_append(s_info.slave_list, slave);
+ return slave;
+}
+
+static inline void invoke_delete_cb(struct slave_node *slave)
+{
+ Eina_List *l;
+ Eina_List *n;
+ struct event *event;
+
+ slave->in_event_process |= SLAVE_EVENT_PROCESS_DELETE;
+ EINA_LIST_FOREACH_SAFE(slave->event_delete_list, l, n, event) {
+ if (event->deleted || event->evt_cb(event->slave, event->cbdata) < 0 || event->deleted) {
+ slave->event_delete_list = eina_list_remove(slave->event_delete_list, event);
+ DbgFree(event);
+ }
+ }
+ slave->in_event_process &= ~SLAVE_EVENT_PROCESS_DELETE;
+}
+
+static inline void destroy_slave_node(struct slave_node *slave)
+{
+ struct event *event;
+ struct priv_data *priv;
+
+ if (slave_pid(slave) != (pid_t)-1) {
+ ErrPrint("Slave is not deactivated\n");
+ return;
+ }
+
+ xmonitor_del_event_callback(XMONITOR_PAUSED, xmonitor_pause_cb, slave);
+ xmonitor_del_event_callback(XMONITOR_RESUMED, xmonitor_resume_cb, slave);
+
+ invoke_delete_cb(slave);
+ slave_rpc_fini(slave); /*!< Finalize the RPC after handling all delete callbacks */
+
+ EINA_LIST_FREE(slave->event_delete_list, event) {
+ DbgFree(event);
+ }
+
+ EINA_LIST_FREE(slave->event_activate_list, event) {
+ DbgFree(event);
+ }
+
+ EINA_LIST_FREE(slave->event_deactivate_list, event) {
+ DbgFree(event);
+ }
+
+ EINA_LIST_FREE(slave->event_fault_list, event) {
+ DbgFree(event);
+ }
+
+ EINA_LIST_FREE(slave->data_list, priv) {
+ DbgFree(priv->tag);
+ DbgFree(priv);
+ }
+
+ s_info.slave_list = eina_list_remove(s_info.slave_list, slave);
+
+ if (slave->ttl_timer) {
+ ecore_timer_del(slave->ttl_timer);
+ }
+
+ if (slave->activate_timer) {
+ ecore_timer_del(slave->activate_timer);
+ }
+
+ if (slave->relaunch_timer) {
+ ecore_timer_del(slave->relaunch_timer);
+ }
+
+ DbgFree(slave->abi);
+ DbgFree(slave->name);
+ DbgFree(slave->pkgname);
+ DbgFree(slave);
+ return;
+}
+
+static inline struct slave_node *find_slave(const char *name)
+{
+ struct slave_node *slave;
+ Eina_List *l;
+
+ EINA_LIST_FOREACH(s_info.slave_list, l, slave) {
+ if (!strcmp(slave->name, name)) {
+ return slave;
+ }
+ }
+
+ return NULL;
+}
+
+HAPI int slave_expired_ttl(struct slave_node *slave)
+{
+ if (!slave) {
+ return 0;
+ }
+
+ if (!slave->secured) {
+ return 0;
+ }
+
+ return !!slave->ttl_timer;
+}
+
+HAPI struct slave_node *slave_ref(struct slave_node *slave)
+{
+ if (!slave) {
+ return NULL;
+ }
+
+ slave->refcnt++;
+ return slave;
+}
+
+HAPI struct slave_node *slave_unref(struct slave_node *slave)
+{
+ if (!slave) {
+ return NULL;
+ }
+
+ if (slave->refcnt == 0) {
+ ErrPrint("Slave refcnt is not valid\n");
+ return NULL;
+ }
+
+ slave->refcnt--;
+ if (slave->refcnt == 0) {
+ destroy_slave_node(slave);
+ slave = NULL;
+ }
+
+ return slave;
+}
+
+HAPI const int const slave_refcnt(struct slave_node *slave)
+{
+ return slave->refcnt;
+}
+
+HAPI struct slave_node *slave_create(const char *name, int is_secured, const char *abi, const char *pkgname, int network)
+{
+ struct slave_node *slave;
+
+ slave = find_slave(name);
+ if (slave) {
+ if (slave->secured != is_secured) {
+ ErrPrint("Exists slave and creating slave's security flag is not matched\n");
+ }
+ return slave;
+ }
+
+ slave = create_slave_node(name, is_secured, abi, pkgname, network);
+ if (!slave) {
+ return NULL;
+ }
+
+ slave_ref(slave);
+ slave_rpc_init(slave);
+
+ return slave;
+}
+
+/*!
+ * \note
+ * Before destroying slave object,
+ * you should check the RPC(slave_async_XXX) state and Private data field (slave_set_data)
+ */
+HAPI void slave_destroy(struct slave_node *slave)
+{
+ slave_unref(slave);
+}
+
+static inline void invoke_fault_cb(struct slave_node *slave)
+{
+ Eina_List *l;
+ Eina_List *n;
+ struct event *event;
+
+ slave->in_event_process |= SLAVE_EVENT_PROCESS_FAULT;
+ EINA_LIST_FOREACH_SAFE(slave->event_fault_list, l, n, event) {
+ if (event->deleted || event->evt_cb(event->slave, event->cbdata) < 0 || event->deleted) {
+ slave->event_fault_list = eina_list_remove(slave->event_fault_list, event);
+ DbgFree(event);
+ }
+ }
+ slave->in_event_process &= ~SLAVE_EVENT_PROCESS_FAULT;
+}
+
+static inline void invoke_activate_cb(struct slave_node *slave)
+{
+ Eina_List *l;
+ Eina_List *n;
+ struct event *event;
+
+ slave->in_event_process |= SLAVE_EVENT_PROCESS_ACTIVATE;
+ EINA_LIST_FOREACH_SAFE(slave->event_activate_list, l, n, event) {
+ if (event->deleted || event->evt_cb(event->slave, event->cbdata) < 0 || event->deleted) {
+ slave->event_activate_list = eina_list_remove(slave->event_activate_list, event);
+ DbgFree(event);
+ }
+ }
+ slave->in_event_process &= ~SLAVE_EVENT_PROCESS_ACTIVATE;
+}
+
+static Eina_Bool activate_timer_cb(void *data)
+{
+ struct slave_node *slave = data;
+
+ if (slave->relaunch_timer) {
+ ecore_timer_del(slave->relaunch_timer);
+ slave->relaunch_timer = NULL;
+ }
+
+ slave->fault_count++;
+ invoke_fault_cb(slave);
+
+ slave_set_reactivation(slave, 0);
+ slave_set_reactivate_instances(slave, 0);
+
+ slave->activate_timer = NULL;
+ if (slave_pid(slave) > 0) {
+ int ret;
+ DbgPrint("Try to terminate PID: %d\n", slave_pid(slave));
+ ret = aul_terminate_pid(slave_pid(slave));
+ if (ret < 0) {
+ ErrPrint("Terminate failed, pid %d (reason: %d)\n", slave_pid(slave), ret);
+ }
+ }
+
+ CRITICAL_LOG("Slave is not activated in %lf sec (slave: %s)\n", SLAVE_ACTIVATE_TIME, slave_name(slave));
+ slave = slave_deactivated(slave);
+ return ECORE_CALLBACK_CANCEL;
+}
+
+static inline void invoke_slave_fault_handler(struct slave_node *slave)
+{
+ slave->fault_count++;
+ invoke_fault_cb(slave);
+
+ slave_set_reactivation(slave, 0);
+ slave_set_reactivate_instances(slave, 0);
+
+ if (slave_pid(slave) > 0) {
+ if ((slave->ctrl_option & PROVIDER_CTRL_MANUAL_TERMINATION) == PROVIDER_CTRL_MANUAL_TERMINATION) {
+ DbgPrint("Manual termination is turned on\n");
+ (void)slave_rpc_disconnect(slave);
+ } else {
+ int ret;
+ DbgPrint("Try to terminate PID: %d\n", slave_pid(slave));
+ ret = aul_terminate_pid(slave_pid(slave));
+ if (ret < 0) {
+ ErrPrint("Terminate failed, pid %d (reason: %d)\n", slave_pid(slave), ret);
+ }
+ }
+ }
+
+ slave->state = SLAVE_TERMINATED;
+}
+
+static Eina_Bool relaunch_timer_cb(void *data)
+{
+ struct slave_node *slave = data;
+ int ret = ECORE_CALLBACK_CANCEL;
+
+ if (!slave->activate_timer) {
+ ErrPrint("Activate timer is not valid\n");
+ slave->relaunch_timer = NULL;
+
+ invoke_slave_fault_handler(slave);
+ } else if (!slave->relaunch_count) {
+ ErrPrint("Relaunch count is exhahausted\n");
+ ecore_timer_del(slave->activate_timer);
+ slave->activate_timer = NULL;
+
+ slave->relaunch_timer = NULL;
+ invoke_slave_fault_handler(slave);
+ } else {
+ bundle *param;
+
+ param = bundle_create();
+ if (!param) {
+ ErrPrint("Failed to create a bundle\n");
+
+ ecore_timer_del(slave->activate_timer);
+ slave->activate_timer = NULL;
+
+ slave->relaunch_timer = NULL;
+
+ invoke_slave_fault_handler(slave);
+ } else {
+ bundle_add(param, BUNDLE_SLAVE_NAME, slave_name(slave));
+ bundle_add(param, BUNDLE_SLAVE_SECURED, slave->secured ? "true" : "false");
+ bundle_add(param, BUNDLE_SLAVE_ABI, slave->abi);
+
+ slave->pid = (pid_t)aul_launch_app(slave_pkgname(slave), param);
+
+ bundle_free(param);
+
+ switch (slave->pid) {
+#if defined(WEARABLE)
+ case AUL_R_EHIDDENFORGUEST: /**< App hidden for guest mode */
+#endif
+ case AUL_R_ENOLAUNCHPAD: /**< no launchpad */
+ case AUL_R_EILLACC: /**< Illegal Access */
+ case AUL_R_EINVAL: /**< Invalid argument */
+ case AUL_R_ENOINIT: /**< AUL handler NOT initialized */
+ case AUL_R_ERROR: /**< General error */
+ CRITICAL_LOG("Failed to launch a new slave %s (%d)\n", slave_name(slave), slave->pid);
+ slave->pid = (pid_t)-1;
+ ecore_timer_del(slave->activate_timer);
+ slave->activate_timer = NULL;
+
+ slave->relaunch_timer = NULL;
+
+ invoke_slave_fault_handler(slave);
+ /* Waiting app-launch result */
+ break;
+ case AUL_R_ETIMEOUT: /**< Timeout */
+ case AUL_R_ECOMM: /**< Comunication Error */
+ case AUL_R_ETERMINATING: /**< application terminating */
+ case AUL_R_ECANCELED: /**< Operation canceled */
+ slave->relaunch_count--;
+
+ CRITICAL_LOG("Try relaunch again %s (%d), %d\n", slave_name(slave), slave->pid, slave->relaunch_count);
+ slave->pid = (pid_t)-1;
+ ret = ECORE_CALLBACK_RENEW;
+ ecore_timer_reset(slave->activate_timer);
+ /* Try again after a few secs later */
+ break;
+ case AUL_R_LOCAL: /**< Launch by himself */
+ case AUL_R_OK: /**< General success */
+ default:
+ DbgPrint("Slave %s is launched with %d as %s\n", slave_pkgname(slave), slave->pid, slave_name(slave));
+ slave->relaunch_timer = NULL;
+ ecore_timer_reset(slave->activate_timer);
+ break;
+ }
+ }
+
+ }
+
+ return ret;
+}
+
+HAPI int slave_activate(struct slave_node *slave)
+{
+ /*!
+ * \note
+ * This check code can be replaced with the slave->state check code
+ * If the slave data has the PID, it means, it is activated
+ * Even if it is in the termiating sequence, it will have the PID
+ * before terminated at last.
+ * So we can use this simple code for checking the slave's last state.
+ * about it is alive? or not.
+ */
+ if (slave_pid(slave) != (pid_t)-1) {
+ if (slave->terminate_timer) {
+ DbgPrint("Clear terminate timer. to reuse (%d)\n", slave->pid);
+ ecore_timer_del(slave->terminate_timer);
+ slave->terminate_timer = NULL;
+ } else if (slave_state(slave) == SLAVE_REQUEST_TO_TERMINATE || slave_state(slave) == SLAVE_REQUEST_TO_DISCONNECT) {
+ slave_set_reactivation(slave, 1);
+ }
+ return LB_STATUS_ERROR_ALREADY;
+ } else if (slave_state(slave) == SLAVE_REQUEST_TO_LAUNCH) {
+ DbgPrint("Slave is already launched: but the AUL is timed out\n");
+ return LB_STATUS_ERROR_ALREADY;
+ }
+
+ if (DEBUG_MODE) {
+ DbgPrint("Debug Mode enabled. name[%s] secured[%d] abi[%s]\n", slave_name(slave), slave->secured, slave->abi);
+ } else {
+ bundle *param;
+
+ slave->relaunch_count = SLAVE_RELAUNCH_COUNT;
+
+ param = bundle_create();
+ if (!param) {
+ ErrPrint("Failed to create a bundle\n");
+ return LB_STATUS_ERROR_FAULT;
+ }
+
+ bundle_add(param, BUNDLE_SLAVE_NAME, slave_name(slave));
+ bundle_add(param, BUNDLE_SLAVE_SECURED, slave->secured ? "true" : "false");
+ bundle_add(param, BUNDLE_SLAVE_ABI, slave->abi);
+
+ slave->pid = (pid_t)aul_launch_app(slave_pkgname(slave), param);
+
+ bundle_free(param);
+
+ switch (slave->pid) {
+#if defined(WEARABLE)
+ case AUL_R_EHIDDENFORGUEST: /**< App hidden for guest mode */
+#endif
+ case AUL_R_ENOLAUNCHPAD: /**< no launchpad */
+ case AUL_R_EILLACC: /**< Illegal Access */
+ case AUL_R_EINVAL: /**< Invalid argument */
+ case AUL_R_ENOINIT: /**< AUL handler NOT initialized */
+ case AUL_R_ERROR: /**< General error */
+ CRITICAL_LOG("Failed to launch a new slave %s (%d)\n", slave_name(slave), slave->pid);
+ slave->pid = (pid_t)-1;
+ /* Waiting app-launch result */
+ break;
+ case AUL_R_ECOMM: /**< Comunication Error */
+ case AUL_R_ETERMINATING: /**< application terminating */
+ case AUL_R_ECANCELED: /**< Operation canceled */
+ case AUL_R_ETIMEOUT: /**< Timeout */
+ CRITICAL_LOG("Try relaunch this soon %s (%d)\n", slave_name(slave), slave->pid);
+ slave->relaunch_timer = ecore_timer_add(SLAVE_RELAUNCH_TIME, relaunch_timer_cb, slave);
+ if (!slave->relaunch_timer) {
+ CRITICAL_LOG("Failed to register a relaunch timer (%s)\n", slave_name(slave));
+ slave->pid = (pid_t)-1;
+ return LB_STATUS_ERROR_FAULT;
+ }
+ /* Try again after a few secs later */
+ break;
+ case AUL_R_LOCAL: /**< Launch by himself */
+ case AUL_R_OK: /**< General success */
+ default:
+ DbgPrint("Slave %s is launched with %d as %s\n", slave_pkgname(slave), slave->pid, slave_name(slave));
+ break;
+ }
+
+ slave->activate_timer = ecore_timer_add(SLAVE_ACTIVATE_TIME, activate_timer_cb, slave);
+ if (!slave->activate_timer) {
+ ErrPrint("Failed to register an activate timer\n");
+ }
+ }
+
+ slave->state = SLAVE_REQUEST_TO_LAUNCH;
+ /*!
+ * \note
+ * Increase the refcnt of a slave,
+ * To prevent from making an orphan(slave).
+ */
+ (void)slave_ref(slave);
+
+ return LB_STATUS_SUCCESS;
+}
+
+HAPI int slave_give_more_ttl(struct slave_node *slave)
+{
+ double delay;
+
+ if (!slave->secured || !slave->ttl_timer) {
+ return LB_STATUS_ERROR_INVALID;
+ }
+
+ delay = SLAVE_TTL - ecore_timer_pending_get(slave->ttl_timer);
+ ecore_timer_delay(slave->ttl_timer, delay);
+ return LB_STATUS_SUCCESS;
+}
+
+HAPI int slave_freeze_ttl(struct slave_node *slave)
+{
+ if (!slave->secured || !slave->ttl_timer) {
+ return LB_STATUS_ERROR_INVALID;
+ }
+
+ ecore_timer_freeze(slave->ttl_timer);
+ return LB_STATUS_SUCCESS;
+}
+
+HAPI int slave_thaw_ttl(struct slave_node *slave)
+{
+ double delay;
+
+ if (!slave->secured || !slave->ttl_timer) {
+ return LB_STATUS_ERROR_INVALID;
+ }
+
+ ecore_timer_thaw(slave->ttl_timer);
+
+ delay = SLAVE_TTL - ecore_timer_pending_get(slave->ttl_timer);
+ ecore_timer_delay(slave->ttl_timer, delay);
+ return LB_STATUS_SUCCESS;
+}
+
+HAPI int slave_activated(struct slave_node *slave)
+{
+ slave->state = SLAVE_RESUMED;
+
+ if (xmonitor_is_paused()) {
+ slave_pause(slave);
+ }
+
+ if (slave->secured == 1 && SLAVE_TTL > 0.0f) {
+ DbgPrint("Slave deactivation timer is added (%s - %lf)\n", slave_name(slave), SLAVE_TTL);
+ slave->ttl_timer = ecore_timer_add(SLAVE_TTL, slave_ttl_cb, slave);
+ if (!slave->ttl_timer) {
+ ErrPrint("Failed to create a TTL timer\n");
+ }
+ }
+
+ invoke_activate_cb(slave);
+
+ slave_set_reactivation(slave, 0);
+ slave_set_reactivate_instances(slave, 0);
+
+#if defined(_USE_ECORE_TIME_GET)
+ slave->activated_at = ecore_time_get();
+#else
+ if (gettimeofday(&slave->activated_at, NULL) < 0) {
+ ErrPrint("Failed to get time of day: %s\n", strerror(errno));
+ slave->activated_at.tv_sec = 0;
+ slave->activated_at.tv_usec = 0;
+ }
+#endif
+
+ if (slave->activate_timer) {
+ ecore_timer_del(slave->activate_timer);
+ slave->activate_timer = NULL;
+ }
+
+ if (slave->relaunch_timer) {
+ ecore_timer_del(slave->relaunch_timer);
+ slave->relaunch_timer = NULL;
+ }
+
+ return LB_STATUS_SUCCESS;
+}
+
+static inline int invoke_deactivate_cb(struct slave_node *slave)
+{
+ Eina_List *l;
+ Eina_List *n;
+ struct event *event;
+ int ret;
+ int reactivate = 0;
+
+ slave->in_event_process |= SLAVE_EVENT_PROCESS_DEACTIVATE;
+
+ EINA_LIST_FOREACH_SAFE(slave->event_deactivate_list, l, n, event) {
+ if (event->deleted) {
+ slave->event_deactivate_list = eina_list_remove(slave->event_deactivate_list, event);
+ DbgFree(event);
+ continue;
+ }
+
+ ret = event->evt_cb(event->slave, event->cbdata);
+ if (ret < 0 || event->deleted) {
+ slave->event_deactivate_list = eina_list_remove(slave->event_deactivate_list, event);
+ DbgFree(event);
+ }
+
+ if (ret == SLAVE_NEED_TO_REACTIVATE) {
+ reactivate++;
+ }
+ }
+
+ slave->in_event_process &= ~SLAVE_EVENT_PROCESS_DEACTIVATE;
+
+ return reactivate;
+}
+
+static Eina_Bool terminate_timer_cb(void *data)
+{
+ struct slave_node *slave = data;
+ int ret;
+
+ /*!
+ * \todo
+ * check the return value of the aul_terminate_pid
+ */
+ slave->state = SLAVE_REQUEST_TO_TERMINATE;
+ slave->terminate_timer = NULL;
+
+ DbgPrint("Terminate slave: %d (%s)\n", slave_pid(slave), slave_name(slave));
+ ret = aul_terminate_pid(slave->pid);
+ if (ret < 0) {
+ ErrPrint("Terminate slave(%s) failed. pid %d (%d)\n", slave_name(slave), slave_pid(slave), ret);
+ slave = slave_deactivated(slave);
+ if (slave == NULL) {
+ DbgPrint("Slave is deleted\n");
+ }
+ }
+
+ return ECORE_CALLBACK_CANCEL;
+}
+
+HAPI struct slave_node *slave_deactivate(struct slave_node *slave, int no_timer)
+{
+ int ret;
+
+ if (!slave_is_activated(slave)) {
+ ErrPrint("Slave is already deactivated\n");
+ if (slave_loaded_instance(slave) == 0) {
+ /*!
+ * \note
+ * If a slave has no more instances,
+ * Destroy it
+ */
+ slave = slave_unref(slave);
+ }
+ return slave;
+ }
+
+ if (slave_pid(slave) <= 0) {
+ return slave;
+ }
+
+ if ((slave->ctrl_option & PROVIDER_CTRL_MANUAL_TERMINATION) == PROVIDER_CTRL_MANUAL_TERMINATION) {
+ /*!
+ * \note
+ * In this case,
+ * The provider requests MANUAL TERMINATION control option.
+ * Master will not send terminate request in this case, the provider should be terminated by itself.
+ */
+ DbgPrint("Manual termination is turned on\n");
+ slave->state = SLAVE_REQUEST_TO_DISCONNECT;
+ (void)slave_rpc_disconnect(slave);
+ } else if (slave->terminate_timer) {
+ ErrPrint("Terminate timer is already fired (%d)\n", slave->pid);
+ } else if (!no_timer) {
+ DbgPrint("Fire the terminate timer: %d\n", slave->pid);
+ slave->terminate_timer = ecore_timer_add(SLAVE_ACTIVATE_TIME, terminate_timer_cb, slave);
+ if (!slave->terminate_timer) {
+ /*!
+ * \note
+ * Normally, Call the terminate_timer_cb directly from here
+ * But in this case. if the aul_terminate_pid failed, Call the slave_deactivated function directly.
+ * Then the "slave" pointer can be changed. To trace it, Copy the body of terminate_timer_cb to here.
+ */
+ ErrPrint("Failed to add a new timer for terminating\n");
+ DbgPrint("Terminate slave: %d (%s)\n", slave_pid(slave), slave_name(slave));
+ /*!
+ * \todo
+ * check the return value of the aul_terminate_pid
+ */
+ slave->state = SLAVE_REQUEST_TO_TERMINATE;
+
+ ret = aul_terminate_pid(slave->pid);
+ if (ret < 0) {
+ ErrPrint("Terminate slave(%s) failed. pid %d (%d)\n", slave_name(slave), slave_pid(slave), ret);
+ slave = slave_deactivated(slave);
+ }
+ }
+ } else {
+ /*!
+ * \todo
+ * check the return value of the aul_terminate_pid
+ */
+ slave->state = SLAVE_REQUEST_TO_TERMINATE;
+
+ DbgPrint("Terminate slave: %d (%s)\n", slave_pid(slave), slave_name(slave));
+ ret = aul_terminate_pid(slave->pid);
+ if (ret < 0) {
+ ErrPrint("Terminate slave(%s) failed. pid %d (%d)\n", slave_name(slave), slave_pid(slave), ret);
+ slave = slave_deactivated(slave);
+ }
+ }
+
+ return slave;
+}
+
+HAPI struct slave_node *slave_deactivated(struct slave_node *slave)
+{
+ int reactivate;
+
+ slave->pid = (pid_t)-1;
+ slave->state = SLAVE_TERMINATED;
+
+ if (slave->ttl_timer) {
+ ecore_timer_del(slave->ttl_timer);
+ slave->ttl_timer = NULL;
+ }
+
+ if (slave->activate_timer) {
+ ecore_timer_del(slave->activate_timer);
+ slave->activate_timer = NULL;
+ }
+
+ if (slave->relaunch_timer) {
+ ecore_timer_del(slave->relaunch_timer);
+ slave->relaunch_timer = NULL;
+ }
+
+ if (slave->terminate_timer) {
+ ecore_timer_del(slave->terminate_timer);
+ slave->terminate_timer = NULL;
+ }
+
+ reactivate = invoke_deactivate_cb(slave);
+
+ slave = slave_unref(slave);
+ if (!slave) {
+ DbgPrint("SLAVE object is destroyed\n");
+ return slave;
+ }
+
+ if ((slave->ctrl_option & PROVIDER_CTRL_MANUAL_REACTIVATION) == PROVIDER_CTRL_MANUAL_REACTIVATION) {
+ /*!
+ * \note
+ * In this case, the provider(Slave) should be reactivated by itself or user.
+ * The master will not reactivate it automatically.
+ */
+ DbgPrint("Manual reactivate option is turned on\n");
+ } else if (reactivate && slave_need_to_reactivate(slave)) {
+ int ret;
+
+ DbgPrint("Need to reactivate a slave\n");
+ ret = slave_activate(slave);
+ if (ret < 0 && ret != LB_STATUS_ERROR_ALREADY) {
+ ErrPrint("Failed to reactivate a slave\n");
+ }
+ } else if (slave_loaded_instance(slave) == 0) {
+ /*!
+ * \note
+ * If a slave has no more instances,
+ * Destroy it
+ */
+ slave = slave_unref(slave);
+ }
+
+ return slave;
+}
+
+HAPI struct slave_node *slave_deactivated_by_fault(struct slave_node *slave)
+{
+ int ret;
+ int reactivate = 1;
+ int reactivate_instances = 1;
+
+ if (!slave_is_activated(slave)) {
+ DbgPrint("Deactivating in progress\n");
+ if (slave_loaded_instance(slave) == 0) {
+ slave = slave_unref(slave);
+ }
+
+ return slave;
+ }
+
+ slave->fault_count++;
+
+ (void)fault_check_pkgs(slave);
+
+ if (slave_pid(slave) > 0) {
+ DbgPrint("Try to terminate PID: %d\n", slave_pid(slave));
+ ret = aul_terminate_pid(slave_pid(slave));
+ if (ret < 0) {
+ ErrPrint("Terminate failed, pid %d\n", slave_pid(slave));
+ }
+ }
+
+#if defined(_USE_ECORE_TIME_GET)
+ double faulted_at;
+
+ faulted_at = ecore_time_get();
+ if (faulted_at - slave->activated_at < MINIMUM_REACTIVATION_TIME) {
+ slave->critical_fault_count++;
+ if (!slave_loaded_instance(slave) || slave->critical_fault_count >= SLAVE_MAX_LOAD) {
+ ErrPrint("Reactivation time is too fast and frequently occurred - Stop to auto reactivation\n");
+ reactivate = 0;
+ reactivate_instances = 0;
+ slave->critical_fault_count = 0;
+ /*!
+ * \note
+ * Fault callback can access the slave information.
+ */
+ invoke_fault_cb(slave);
+ } else {
+ slave->critical_fault_count = 0;
+ }
+ }
+#else
+ struct timeval faulted_at;
+
+ if (gettimeofday(&faulted_at, NULL) == 0) {
+ struct timeval rtv;
+
+ timersub(&faulted_at, &slave->activated_at, &rtv);
+ if (rtv.tv_sec < MINIMUM_REACTIVATION_TIME) {
+ slave->critical_fault_count++;
+ if (!slave_loaded_instance(slave) || slave->critical_fault_count >= SLAVE_MAX_LOAD) {
+ ErrPrint("Reactivation time is too fast and frequently occurred - Stop to auto reactivation\n");
+ reactivate = 0;
+ reactivate_instances = 0;
+ slave->critical_fault_count = 0;
+ /*!
+ * \note
+ * Fault callback can access the slave information.
+ */
+ invoke_fault_cb(slave);
+ }
+ } else {
+ slave->critical_fault_count = 0;
+ }
+ } else {
+ ErrPrint("Failed to get time of day: %s\n", strerror(errno));
+ }
+#endif
+
+ slave_set_reactivation(slave, reactivate);
+ slave_set_reactivate_instances(slave, reactivate_instances);
+
+ slave = slave_deactivated(slave);
+ return slave;
+}
+
+HAPI const int const slave_is_activated(struct slave_node *slave)
+{
+ switch (slave->state) {
+ case SLAVE_REQUEST_TO_TERMINATE:
+ case SLAVE_TERMINATED:
+ return 0;
+ case SLAVE_REQUEST_TO_DISCONNECT:
+ /* This case should be treated as an activated state.
+ * To send the last request to the provider.
+ */
+ case SLAVE_REQUEST_TO_LAUNCH:
+ /* Not yet launched. but the slave incurred an unexpected error */
+ case SLAVE_REQUEST_TO_PAUSE:
+ case SLAVE_REQUEST_TO_RESUME:
+ case SLAVE_PAUSED:
+ case SLAVE_RESUMED:
+ return 1;
+ default:
+ return slave->pid != (pid_t)-1;
+ }
+
+ /* Could not be reach to here */
+ return 0;
+}
+
+HAPI int slave_event_callback_add(struct slave_node *slave, enum slave_event event, int (*cb)(struct slave_node *, void *), void *data)
+{
+ struct event *ev;
+
+ ev = calloc(1, sizeof(*ev));
+ if (!ev) {
+ ErrPrint("Heap: %s\n", strerror(errno));
+ return LB_STATUS_ERROR_MEMORY;
+ }
+
+ ev->slave = slave;
+ ev->cbdata = data;
+ ev->evt_cb = cb;
+
+ /*!
+ * \note
+ * Use the eina_list_prepend API.
+ * To keep the sequence of a callback invocation.
+ *
+ * Here is an example sequence.
+ *
+ * slave_event_callback_add(CALLBACK_01);
+ * slave_event_callback_add(CALLBACK_02);
+ * slave_event_callback_add(CALLBACK_03);
+ *
+ * Then the invoke_event_callback function will call the CALLBACKS as below sequence
+ *
+ * invoke_CALLBACK_03
+ * invoke_CALLBACK_02
+ * invoke_CALLBACK_01
+ */
+
+ switch (event) {
+ case SLAVE_EVENT_ACTIVATE:
+ slave->event_activate_list = eina_list_prepend(slave->event_activate_list, ev);
+ break;
+ case SLAVE_EVENT_DELETE:
+ slave->event_delete_list = eina_list_prepend(slave->event_delete_list, ev);
+ break;
+ case SLAVE_EVENT_DEACTIVATE:
+ slave->event_deactivate_list = eina_list_prepend(slave->event_deactivate_list, ev);
+ break;
+ case SLAVE_EVENT_PAUSE:
+ slave->event_pause_list = eina_list_prepend(slave->event_pause_list, ev);
+ break;
+ case SLAVE_EVENT_RESUME:
+ slave->event_resume_list = eina_list_prepend(slave->event_resume_list, ev);
+ break;
+ case SLAVE_EVENT_FAULT:
+ slave->event_fault_list = eina_list_prepend(slave->event_fault_list, ev);
+ break;
+ default:
+ DbgFree(ev);
+ return LB_STATUS_ERROR_INVALID;
+ }
+
+ return LB_STATUS_SUCCESS;
+}
+
+HAPI int slave_event_callback_del(struct slave_node *slave, enum slave_event event, int (*cb)(struct slave_node *, void *), void *data)
+{
+ struct event *ev;
+ Eina_List *l;
+ Eina_List *n;
+
+ switch (event) {
+ case SLAVE_EVENT_DEACTIVATE:
+ EINA_LIST_FOREACH_SAFE(slave->event_deactivate_list, l, n, ev) {
+ if (ev->evt_cb == cb && ev->cbdata == data) {
+ if (slave->in_event_process & SLAVE_EVENT_PROCESS_DEACTIVATE) {
+ ev->deleted = 1;
+ } else {
+ slave->event_deactivate_list = eina_list_remove(slave->event_deactivate_list, ev);
+ DbgFree(ev);
+ }
+ return LB_STATUS_SUCCESS;
+ }
+ }
+ break;
+ case SLAVE_EVENT_DELETE:
+ EINA_LIST_FOREACH_SAFE(slave->event_delete_list, l, n, ev) {
+ if (ev->evt_cb == cb && ev->cbdata == data) {
+ if (slave->in_event_process & SLAVE_EVENT_PROCESS_DELETE) {
+ ev->deleted = 1;
+ } else {
+ slave->event_delete_list = eina_list_remove(slave->event_delete_list, ev);
+ DbgFree(ev);
+ }
+ return LB_STATUS_SUCCESS;
+ }
+ }
+ break;
+ case SLAVE_EVENT_ACTIVATE:
+ EINA_LIST_FOREACH_SAFE(slave->event_activate_list, l, n, ev) {
+ if (ev->evt_cb == cb && ev->cbdata == data) {
+ if (slave->in_event_process & SLAVE_EVENT_PROCESS_ACTIVATE) {
+ ev->deleted = 1;
+ } else {
+ slave->event_activate_list = eina_list_remove(slave->event_activate_list, ev);
+ DbgFree(ev);
+ }
+ return LB_STATUS_SUCCESS;
+ }
+ }
+ break;
+ case SLAVE_EVENT_PAUSE:
+ EINA_LIST_FOREACH_SAFE(slave->event_pause_list, l, n, ev) {
+ if (ev->evt_cb == cb && ev->cbdata == data) {
+ if (slave->in_event_process & SLAVE_EVENT_PROCESS_PAUSE) {
+ ev->deleted = 1;
+ } else {
+ slave->event_pause_list = eina_list_remove(slave->event_pause_list, ev);
+ DbgFree(ev);
+ }
+ return LB_STATUS_SUCCESS;
+ }
+ }
+ break;
+ case SLAVE_EVENT_RESUME:
+ EINA_LIST_FOREACH_SAFE(slave->event_resume_list, l, n, ev) {
+ if (ev->evt_cb == cb && ev->cbdata == data) {
+ if (slave->in_event_process & SLAVE_EVENT_PROCESS_RESUME) {
+ ev->deleted = 1;
+ } else {
+ slave->event_resume_list = eina_list_remove(slave->event_resume_list, ev);
+ DbgFree(ev);
+ }
+ return LB_STATUS_SUCCESS;
+ }
+ }
+ break;
+ case SLAVE_EVENT_FAULT:
+ EINA_LIST_FOREACH_SAFE(slave->event_fault_list, l, n, ev) {
+ if (ev->evt_cb == cb && ev->cbdata == data) {
+ if (slave->in_event_process & SLAVE_EVENT_PROCESS_FAULT) {
+ ev->deleted = 1;
+ } else {
+ slave->event_fault_list = eina_list_remove(slave->event_fault_list, ev);
+ DbgFree(ev);
+ }
+ return LB_STATUS_SUCCESS;
+ }
+ }
+ break;
+ default:
+ return LB_STATUS_ERROR_INVALID;
+ }
+
+ return LB_STATUS_ERROR_NOT_EXIST;
+}
+
+HAPI int slave_set_data(struct slave_node *slave, const char *tag, void *data)
+{
+ struct priv_data *priv;
+
+ priv = calloc(1, sizeof(*priv));
+ if (!priv) {
+ ErrPrint("Heap: %s\n", strerror(errno));
+ return LB_STATUS_ERROR_MEMORY;
+ }
+
+ priv->tag = strdup(tag);
+ if (!priv->tag) {
+ ErrPrint("Heap: %s\n", strerror(errno));
+ DbgFree(priv);
+ return LB_STATUS_ERROR_MEMORY;
+ }
+
+ priv->data = data;
+ slave->data_list = eina_list_append(slave->data_list, priv);
+ return LB_STATUS_SUCCESS;
+}
+
+HAPI void *slave_del_data(struct slave_node *slave, const char *tag)
+{
+ struct priv_data *priv;
+ void *data;
+ Eina_List *l;
+ Eina_List *n;
+
+ EINA_LIST_FOREACH_SAFE(slave->data_list, l, n, priv) {
+ if (!strcmp(priv->tag, tag)) {
+ slave->data_list = eina_list_remove(slave->data_list, priv);
+
+ data = priv->data;
+ DbgFree(priv->tag);
+ DbgFree(priv);
+ return data;
+ }
+ }
+
+ return NULL;
+}
+
+HAPI void *slave_data(struct slave_node *slave, const char *tag)
+{
+ struct priv_data *priv;
+ Eina_List *l;
+
+ EINA_LIST_FOREACH(slave->data_list, l, priv) {
+ if (!strcmp(priv->tag, tag)) {
+ return priv->data;
+ }
+ }
+
+ return NULL;
+}
+
+HAPI struct slave_node *slave_find_by_pid(pid_t pid)
+{
+ Eina_List *l;
+ struct slave_node *slave;
+
+ EINA_LIST_FOREACH(s_info.slave_list, l, slave) {
+ if (slave_pid(slave) == pid) {
+ return slave;
+ }
+ }
+
+ return NULL;
+}
+
+HAPI struct slave_node *slave_find_by_name(const char *name)
+{
+ Eina_List *l;
+ struct slave_node *slave;
+
+ EINA_LIST_FOREACH(s_info.slave_list, l, slave) {
+ if (!strcmp(slave_name(slave), name)) {
+ return slave;
+ }
+ }
+
+ return NULL;
+}
+
+HAPI struct slave_node *slave_find_available(const char *abi, int secured, int network)
+{
+ Eina_List *l;
+ struct slave_node *slave;
+
+ EINA_LIST_FOREACH(s_info.slave_list, l, slave) {
+ if (slave->secured != secured) {
+ continue;
+ }
+
+ if ((slave->state == SLAVE_REQUEST_TO_TERMINATE || slave->state == SLAVE_REQUEST_TO_DISCONNECT) && slave->loaded_instance == 0) {
+ /*!
+ * \note
+ * If a slave is in request_to_terminate state,
+ * and the slave object has no more intances,
+ * the slave object will be deleted soon.
+ * so we cannot reuse it.
+ *
+ * This object is not usable.
+ */
+ continue;
+ }
+
+ if (strcasecmp(slave->abi, abi)) {
+ continue;
+ }
+
+ if (slave->secured) {
+ if (slave->loaded_package == 0) {
+ DbgPrint("Found secured slave - has no instances (%s)\n", slave_name(slave));
+ return slave;
+ }
+ } else if (slave->network == network) {
+ DbgPrint("slave[%s] loaded_package[%d] net: [%d]\n", slave_name(slave), slave->loaded_package, slave->network);
+ if (!strcasecmp(abi, DEFAULT_ABI)) {
+ if (slave->loaded_package < SLAVE_MAX_LOAD) {
+ return slave;
+ }
+ } else {
+ return slave;
+ }
+ }
+ }
+
+ return NULL;
+}
+
+HAPI struct slave_node *slave_find_by_pkgname(const char *pkgname)
+{
+ Eina_List *l;
+ struct slave_node *slave;
+
+ EINA_LIST_FOREACH(s_info.slave_list, l, slave) {
+ if (!strcmp(slave_pkgname(slave), pkgname)) {
+ if (slave_pid(slave) == (pid_t)-1) {
+ return slave;
+ }
+ }
+ }
+
+ return NULL;
+}
+
+HAPI struct slave_node *slave_find_by_rpc_handle(int handle)
+{
+ Eina_List *l;
+ struct slave_node *slave;
+
+ if (handle <= 0) {
+ ErrPrint("Invalid RPC handle: %d\n", handle);
+ return NULL;
+ }
+
+ EINA_LIST_FOREACH(s_info.slave_list, l, slave) {
+ if (slave_rpc_handle(slave) == handle) {
+ return slave;
+ }
+ }
+
+ /* Not found */
+ return NULL;
+}
+
+HAPI void slave_load_package(struct slave_node *slave)
+{
+ slave->loaded_package++;
+}
+
+HAPI void slave_unload_package(struct slave_node *slave)
+{
+ if (!slave || slave->loaded_package == 0) {
+ ErrPrint("Slave loaded package is not correct\n");
+ return;
+ }
+
+ slave->loaded_package--;
+}
+
+HAPI void slave_load_instance(struct slave_node *slave)
+{
+ slave->loaded_instance++;
+ DbgPrint("Instance: (%d)%d\n", slave_pid(slave), slave->loaded_instance);
+}
+
+HAPI int const slave_loaded_instance(struct slave_node *slave)
+{
+ return slave->loaded_instance;
+}
+
+HAPI int const slave_loaded_package(struct slave_node *slave)
+{
+ return slave->loaded_package;
+}
+
+HAPI struct slave_node *slave_unload_instance(struct slave_node *slave)
+{
+ if (!slave || slave->loaded_instance == 0) {
+ ErrPrint("Slave loaded instance is not correct\n");
+ return slave;
+ }
+
+ slave->loaded_instance--;
+ DbgPrint("Instance: (%d)%d\n", slave_pid(slave), slave->loaded_instance);
+ if (slave->loaded_instance == 0 && slave_is_activated(slave)) {
+ slave_set_reactivation(slave, 0);
+ slave_set_reactivate_instances(slave, 0);
+
+ slave = slave_deactivate(slave, 0);
+ }
+
+ return slave;
+}
+
+HAPI const int const slave_is_secured(const struct slave_node *slave)
+{
+ return slave->secured;
+}
+
+HAPI const char * const slave_name(const struct slave_node *slave)
+{
+ return slave->name;
+}
+
+HAPI const char * const slave_abi(const struct slave_node *slave)
+{
+ return slave->abi;
+}
+
+HAPI const pid_t const slave_pid(const struct slave_node *slave)
+{
+ return slave->pid;
+}
+
+HAPI int slave_set_pid(struct slave_node *slave, pid_t pid)
+{
+ if (!slave) {
+ return LB_STATUS_ERROR_INVALID;
+ }
+
+ DbgPrint("Slave PID is updated to %d from %d\n", pid, slave_pid(slave));
+
+ slave->pid = pid;
+ return LB_STATUS_SUCCESS;
+}
+
+static inline void invoke_resumed_cb(struct slave_node *slave)
+{
+ Eina_List *l;
+ Eina_List *n;
+ struct event *event;
+
+ slave->in_event_process |= SLAVE_EVENT_PROCESS_RESUME;
+ EINA_LIST_FOREACH_SAFE(slave->event_resume_list, l, n, event) {
+ if (event->deleted || event->evt_cb(event->slave, event->cbdata) < 0 || event->deleted) {
+ slave->event_resume_list = eina_list_remove(slave->event_resume_list, event);
+ DbgFree(event);
+ }
+ }
+ slave->in_event_process &= ~SLAVE_EVENT_PROCESS_RESUME;
+}
+
+static void resume_cb(struct slave_node *slave, const struct packet *packet, void *data)
+{
+ int ret;
+
+ if (slave->state == SLAVE_REQUEST_TO_TERMINATE || slave->state == SLAVE_REQUEST_TO_DISCONNECT) {
+ DbgPrint("Slave is terminating now. ignore resume result\n");
+ return;
+ }
+
+ if (!packet) {
+ ErrPrint("Failed to change the state of the slave\n");
+ slave->state = SLAVE_PAUSED;
+ return;
+ }
+
+ if (packet_get(packet, "i", &ret) != 1) {
+ ErrPrint("Invalid parameter\n");
+ return;
+ }
+
+ if (ret == 0) {
+ slave->state = SLAVE_RESUMED;
+ slave_rpc_ping_thaw(slave);
+ invoke_resumed_cb(slave);
+ }
+}
+
+static inline void invoke_paused_cb(struct slave_node *slave)
+{
+ Eina_List *l;
+ Eina_List *n;
+ struct event *event;
+
+ slave->in_event_process |= SLAVE_EVENT_PROCESS_PAUSE;
+ EINA_LIST_FOREACH_SAFE(slave->event_pause_list, l, n, event) {
+ if (event->deleted || event->evt_cb(event->slave, event->cbdata) < 0 || event->deleted) {
+ slave->event_pause_list = eina_list_remove(slave->event_pause_list, event);
+ DbgFree(event);
+ }
+ }
+ slave->in_event_process &= ~SLAVE_EVENT_PROCESS_PAUSE;
+}
+
+static void pause_cb(struct slave_node *slave, const struct packet *packet, void *data)
+{
+ int ret;
+
+ if (slave->state == SLAVE_REQUEST_TO_TERMINATE || slave->state == SLAVE_REQUEST_TO_DISCONNECT) {
+ DbgPrint("Slave is terminating now. ignore pause result\n");
+ return;
+ }
+
+ if (!packet) {
+ ErrPrint("Failed to change the state of the slave\n");
+ slave->state = SLAVE_RESUMED;
+ return;
+ }
+
+ if (packet_get(packet, "i", &ret) != 1) {
+ ErrPrint("Invalid parameter\n");
+ return;
+ }
+
+ if (ret == 0) {
+ slave->state = SLAVE_PAUSED;
+ slave_rpc_ping_freeze(slave);
+ invoke_paused_cb(slave);
+ }
+}
+
+HAPI int slave_resume(struct slave_node *slave)
+{
+ double timestamp;
+ struct packet *packet;
+
+ switch (slave->state) {
+ case SLAVE_REQUEST_TO_DISCONNECT:
+ case SLAVE_REQUEST_TO_LAUNCH:
+ case SLAVE_REQUEST_TO_TERMINATE:
+ case SLAVE_TERMINATED:
+ return LB_STATUS_ERROR_INVALID;
+ case SLAVE_RESUMED:
+ case SLAVE_REQUEST_TO_RESUME:
+ return LB_STATUS_SUCCESS;
+ default:
+ break;
+ }
+
+ timestamp = util_timestamp();
+
+ packet = packet_create("resume", "d", timestamp);
+ if (!packet) {
+ ErrPrint("Failed to prepare param\n");
+ return LB_STATUS_ERROR_FAULT;
+ }
+
+ slave->state = SLAVE_REQUEST_TO_RESUME;
+ return slave_rpc_async_request(slave, NULL, packet, resume_cb, NULL, 0);
+}
+
+HAPI int slave_pause(struct slave_node *slave)
+{
+ double timestamp;
+ struct packet *packet;
+
+ switch (slave->state) {
+ case SLAVE_REQUEST_TO_DISCONNECT:
+ case SLAVE_REQUEST_TO_LAUNCH:
+ case SLAVE_REQUEST_TO_TERMINATE:
+ case SLAVE_TERMINATED:
+ return LB_STATUS_ERROR_INVALID;
+ case SLAVE_PAUSED:
+ case SLAVE_REQUEST_TO_PAUSE:
+ return LB_STATUS_SUCCESS;
+ default:
+ break;
+ }
+
+ timestamp = util_timestamp();
+
+ packet = packet_create("pause", "d", timestamp);
+ if (!packet) {
+ ErrPrint("Failed to prepare param\n");
+ return LB_STATUS_ERROR_FAULT;
+ }
+
+ slave->state = SLAVE_REQUEST_TO_PAUSE;
+ return slave_rpc_async_request(slave, NULL, packet, pause_cb, NULL, 0);
+}
+
+HAPI const char *slave_pkgname(const struct slave_node *slave)
+{
+ return slave ? slave->pkgname : NULL;
+}
+
+HAPI enum slave_state slave_state(const struct slave_node *slave)
+{
+ return slave ? slave->state : SLAVE_ERROR;
+}
+
+HAPI const char *slave_state_string(const struct slave_node *slave)
+{
+ switch (slave->state) {
+ case SLAVE_REQUEST_TO_DISCONNECT:
+ return "RequestToDisconnect";
+ case SLAVE_REQUEST_TO_LAUNCH:
+ return "RequestToLaunch";
+ case SLAVE_REQUEST_TO_TERMINATE:
+ return "RequestToTerminate";
+ case SLAVE_TERMINATED:
+ return "Terminated";
+ case SLAVE_REQUEST_TO_PAUSE:
+ return "RequestToPause";
+ case SLAVE_REQUEST_TO_RESUME:
+ return "RequestToResume";
+ case SLAVE_PAUSED:
+ return "Paused";
+ case SLAVE_RESUMED:
+ return "Resumed";
+ case SLAVE_ERROR:
+ return "Error";
+ default:
+ break;
+ }
+
+ return "Unknown";
+}
+
+HAPI const void *slave_list(void)
+{
+ return s_info.slave_list;
+}
+
+HAPI int const slave_fault_count(const struct slave_node *slave)
+{
+ return slave->fault_count;
+}
+
+HAPI double const slave_ttl(const struct slave_node *slave)
+{
+ if (!slave->ttl_timer) {
+ return 0.0f;
+ }
+
+ return ecore_timer_pending_get(slave->ttl_timer);
+}
+
+HAPI void slave_set_reactivate_instances(struct slave_node *slave, int reactivate)
+{
+ slave->reactivate_instances = reactivate;
+}
+
+HAPI int slave_need_to_reactivate_instances(struct slave_node *slave)
+{
+ return slave->reactivate_instances;
+}
+
+HAPI void slave_set_reactivation(struct slave_node *slave, int flag)
+{
+ slave->reactivate_slave = flag;
+}
+
+HAPI int slave_need_to_reactivate(struct slave_node *slave)
+{
+ return slave->reactivate_slave;
+}
+
+HAPI int slave_network(const struct slave_node *slave)
+{
+ return slave->network;
+}
+
+HAPI void slave_set_network(struct slave_node *slave, int network)
+{
+ slave->network = network;
+}
+
+HAPI int slave_deactivate_all(int reactivate, int reactivate_instances, int no_timer)
+{
+ Eina_List *l;
+ Eina_List *n;
+ struct slave_node *slave;
+ int cnt = 0;
+
+ s_info.deactivate_all_refcnt++;
+ if (s_info.deactivate_all_refcnt > 1) {
+ return 0;
+ }
+ DbgPrint("Deactivate all\n");
+
+ EINA_LIST_FOREACH_SAFE(s_info.slave_list, l, n, slave) {
+ slave_set_reactivate_instances(slave, reactivate_instances);
+ slave_set_reactivation(slave, reactivate);
+
+ if (!slave_deactivate(slave, no_timer)) {
+ s_info.slave_list = eina_list_remove(s_info.slave_list, slave);
+ }
+
+ cnt++;
+ }
+
+ return cnt;
+}
+
+HAPI int slave_activate_all(void)
+{
+ Eina_List *l;
+ struct slave_node *slave;
+ int cnt = 0;
+
+ s_info.deactivate_all_refcnt--;
+ if (s_info.deactivate_all_refcnt > 0) {
+ return 0;
+ }
+ DbgPrint("Activate all\n");
+
+ EINA_LIST_FOREACH(s_info.slave_list, l, slave) {
+ slave_activate(slave);
+ cnt++;
+ }
+
+ return cnt;
+}
+
+HAPI void slave_set_control_option(struct slave_node *slave, int ctrl)
+{
+ slave->ctrl_option = ctrl;
+}
+
+HAPI int slave_control_option(struct slave_node *slave)
+{
+ return slave->ctrl_option;
+}
+
+/* End of a file */