322 lines
8.6 KiB
C
322 lines
8.6 KiB
C
#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
|
|
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);
|
|
}
|
|
}
|
|
|