#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
" "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); } }