You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

321 lines
8.6 KiB

#include "e.h"
typedef struct
{
char cmd[24];
int size;
} Message_Head;
typedef struct
{
void (*func) (void *data, const char *params);
void *data;
Eina_Bool delete_me : 1;
} Handler;
static Ecore_Exe *_system_exe = NULL;
static Ecore_Event_Handler *_handler_del = NULL;
static Ecore_Event_Handler *_handler_data = NULL;
static Eina_Binbuf *_msg_buf = NULL;
static Eina_Hash *_handlers = NULL;
static int _handlers_busy = 0;
static Ecore_Timer *_error_dialog_timer = NULL;
static double _last_spawn = 0.0;
static int _respawn_count = 0;
static Eina_Bool
_cb_dialog_timer(void *data EINA_UNUSED)
{
_error_dialog_timer = NULL;
e_util_dialog_show(_("Error in Enlightenment System Service"),
_("Enlightenment cannot successfully start<br>"
"the enlightenment_system service."));
return EINA_FALSE;
}
static void
_system_spawn_error(void)
{
if (_error_dialog_timer) ecore_timer_del(_error_dialog_timer);
_error_dialog_timer = ecore_timer_add(5.0, _cb_dialog_timer, NULL);
}
static void
_system_spawn(void)
{
char buf[PATH_MAX];
double t = ecore_time_get();
if ((t - _last_spawn) < 10.0) _respawn_count++;
else _respawn_count = 0;
if (_respawn_count > 5) return;
snprintf(buf, sizeof(buf),
"%s/enlightenment/utils/enlightenment_system", e_prefix_lib_get());
_system_exe = ecore_exe_pipe_run
(buf, ECORE_EXE_NOT_LEADER | ECORE_EXE_TERM_WITH_PARENT |
ECORE_EXE_PIPE_READ | ECORE_EXE_PIPE_WRITE, NULL);
if (!_system_exe) _system_spawn_error();
}
static Eina_Bool
_system_message_read(void)
{
Message_Head *head;
const void *bdata = eina_binbuf_string_get(_msg_buf);
const char *data = bdata;
size_t len = eina_binbuf_length_get(_msg_buf);
Eina_Binbuf *buf2;
if (!data) return EINA_FALSE;
if (len < sizeof(Message_Head)) return EINA_FALSE;
head = (Message_Head *)bdata;
if (len < (sizeof(Message_Head) + head->size)) return EINA_FALSE;
if (_handlers)
{
Eina_List *list, *l, *ll, *plist;
Handler *h;
int del_count = 0;
_handlers_busy++;
list = plist = eina_hash_find(_handlers, head->cmd);
EINA_LIST_FOREACH(list, l, h)
{
if (!h->delete_me)
{
if (head->size == 0) h->func(h->data, NULL);
else
{
if ((data + sizeof(Message_Head))[head->size - 1] == 0)
h->func(h->data,
(const char *)(data + sizeof(Message_Head)));
}
}
}
_handlers_busy--;
if (_handlers_busy == 0)
{
EINA_LIST_FOREACH_SAFE(list, l, ll, h)
{
if (h->delete_me)
{
list = eina_list_remove_list(list, l);
del_count++;
free(h);
}
}
}
if (del_count > 0)
{
eina_hash_del(_handlers, head->cmd, plist);
eina_hash_add(_handlers, head->cmd, list);
}
}
buf2 = eina_binbuf_new();
if (buf2)
{
eina_binbuf_append_length
(buf2,
(const unsigned char *)data + sizeof(Message_Head) + head->size,
len - (sizeof(Message_Head) + head->size));
eina_binbuf_free(_msg_buf);
_msg_buf = buf2;
}
return EINA_TRUE;
}
static Eina_Bool
_cb_exe_del(void *data EINA_UNUSED, int ev_type EINA_UNUSED, void *event)
{
Ecore_Exe_Event_Del *ev = event;
if (ev->exe != _system_exe) return ECORE_CALLBACK_PASS_ON;
if ((ev->exit_code == 3) || (ev->exit_code == 5) ||
(ev->exit_code == 7) || (ev->exit_code == 9) ||
(ev->exit_code == 11))
{
// didn't run because it literally refused....
_system_spawn_error();
}
else
{
// it died for some other reason - restart it - maybe crashed?
if (_msg_buf) eina_binbuf_free(_msg_buf);
_msg_buf = eina_binbuf_new();
_system_spawn();
}
return ECORE_CALLBACK_DONE;
}
static Eina_Bool
_cb_exe_data(void *data EINA_UNUSED, int ev_type EINA_UNUSED, void *event)
{
Ecore_Exe_Event_Data *ev = event;
if (ev->exe != _system_exe) return ECORE_CALLBACK_PASS_ON;
// this is dumb, but it will work as this is avery low bandwidth
// i/o channel to/from the child exe stdin/out
if (_msg_buf)
{
eina_binbuf_append_length(_msg_buf, ev->data, ev->size);
while (_msg_buf && _system_message_read());
}
return ECORE_CALLBACK_DONE;
}
static Eina_Bool
_handler_list_free(const Eina_Hash *hash EINA_UNUSED, const void *key EINA_UNUSED, void *data, void *fdata EINA_UNUSED)
{
Eina_List *list = data;
Handler *h;
EINA_LIST_FREE(list, h) free(h);
return EINA_TRUE;
}
/* externally accessible functions */
EINTERN int
e_system_init(void)
{
// XXX:
//
// if exe_data - parse/get data
// ... per message - call registered cb's for that msg
_handler_del = ecore_event_handler_add(ECORE_EXE_EVENT_DEL, _cb_exe_del, NULL);
_handler_data = ecore_event_handler_add(ECORE_EXE_EVENT_DATA, _cb_exe_data, NULL);
_msg_buf = eina_binbuf_new();
_handlers = eina_hash_string_superfast_new(NULL);
_system_spawn();
return 1;
}
EINTERN int
e_system_shutdown(void)
{
if (_msg_buf) eina_binbuf_free(_msg_buf);
if (_handler_del) ecore_event_handler_del(_handler_del);
if (_handler_data) ecore_event_handler_del(_handler_data);
if (_system_exe)
{
ecore_exe_interrupt(_system_exe);
ecore_exe_quit(_system_exe);
ecore_exe_terminate(_system_exe);
ecore_exe_kill(_system_exe);
ecore_exe_free(_system_exe);
}
if (_handlers)
{
eina_hash_foreach(_handlers, _handler_list_free, NULL);
eina_hash_free(_handlers);
}
if (_error_dialog_timer) ecore_timer_del(_error_dialog_timer);
_msg_buf = NULL;
_handler_del = NULL;
_handler_data = NULL;
_system_exe = NULL;
_handlers = NULL;
_error_dialog_timer = NULL;
return 1;
}
E_API void EINA_PRINTF(2, 3)
e_system_send(const char *cmd, const char *fmt, ...)
{
char *buf = NULL, stack_buf[4096];
Message_Head head;
size_t len = strlen(cmd);
int printed = 0;
va_list ap;
if (!_system_exe)
{
ERR("Trying to send system command to non-existent process");
return;
}
if (len > 23)
{
ERR("Trying to send command of length %i (max 23)", (int)len);
return;
}
if (fmt)
{
buf = stack_buf;
va_start(ap, fmt);
printed = vsnprintf(stack_buf, sizeof(stack_buf), fmt, ap);
va_end(ap);
if ((size_t)printed >= (sizeof(stack_buf) - 1))
{
buf = malloc(printed + 1);
if (!buf) goto end;
va_start(ap, fmt);
printed = vsnprintf(buf, printed + 1, fmt, ap);
va_end(ap);
}
}
memset(head.cmd, 0, sizeof(head.cmd));
memcpy(head.cmd, cmd, len);
if (printed > 0) head.size = printed + 1;
else head.size = 0;
ecore_exe_send(_system_exe, &head, sizeof(head));
if ((buf) && (head.size > 0))
{
ecore_exe_send(_system_exe, buf, head.size);
}
end:
if (buf != stack_buf) free(buf);
}
E_API void
e_system_handler_add(const char *cmd, void (*func) (void *data, const char *params), void *data)
{
Eina_List *list;
Handler *h;
if (!_handlers)
{
ERR("Trying to add system handler to NULL handler hash");
return;
}
h = calloc(1, sizeof(Handler));
if (!h) return;
h->func = func;
h->data = data;
list = eina_hash_find(_handlers, cmd);
eina_hash_del(_handlers, cmd, list);
list = eina_list_append(list, h);
eina_hash_add(_handlers, cmd, list);
}
E_API void
e_system_handler_del(const char *cmd, void (*func) (void *data, const char *params), void *data)
{
Eina_List *list, *l;
Handler *h;
if (!_handlers)
{
ERR("Trying to del system handler to NULL handler hash");
return;
}
list = eina_hash_find(_handlers, cmd);
if (list)
{
eina_hash_del(_handlers, cmd, list);
EINA_LIST_FOREACH(list, l, h)
{
if ((h->func == func) && (h->data == data))
{
if (_handlers_busy > 0)
h->delete_me = EINA_TRUE;
else
{
list = eina_list_remove_list(list, l);
free(h);
}
break;
}
}
eina_hash_add(_handlers, cmd, list);
}
}