enlightenment/src/bin/e_system.c

419 lines
15 KiB
C
Raw Normal View History

#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)
{
int exit_code = (int)(long)data;
_error_dialog_timer = NULL;
if (exit_code == 0) // can't even do ecore_exe_run...
e_util_dialog_show(_("Error in Enlightenment System Service"),
_("Enlightenment cannot successfully start<br>"
"the enlightenment_system service<br>"
"because ecore_exe_run() failed."));
else if (exit_code == 31) // can't get passwd entry for user
e_util_dialog_show(_("Error in Enlightenment System Service"),
_("Enlightenment cannot successfully start<br>"
"the enlightenment_system service<br>"
"because your user has no passwd file entry."));
else if (exit_code == 32) // username is blank in passwd entry
e_util_dialog_show(_("Error in Enlightenment System Service"),
_("Enlightenment cannot successfully start<br>"
"the enlightenment_system service<br>"
"because your username is blank in the passwd file."));
else if (exit_code == 33) // can't alloc mem for username
e_util_dialog_show(_("Error in Enlightenment System Service"),
_("Enlightenment cannot successfully start<br>"
"the enlightenment_system service<br>"
"because it could not allocate memory."));
else if (exit_code == 34) // can't get group entry for user's group
e_util_dialog_show(_("Error in Enlightenment System Service"),
_("Enlightenment cannot successfully start<br>"
"the enlightenment_system service<br>"
"because it can't find your user group entry."));
else if (exit_code == 35) // group name is blank
e_util_dialog_show(_("Error in Enlightenment System Service"),
_("Enlightenment cannot successfully start<br>"
"the enlightenment_system service<br>"
"because your user group entry is blank."));
else if (exit_code == 36) // can't setuid to 0
e_util_dialog_show(_("Error in Enlightenment System Service"),
_("Enlightenment cannot successfully start<br>"
"the enlightenment_system service<br>"
"since it can't become root. Missing setuid bit?"));
else if (exit_code == 37) // can't setgid to 0
e_util_dialog_show(_("Error in Enlightenment System Service"),
_("Enlightenment cannot successfully start<br>"
"the enlightenment_system service<br>"
"since it can't become group root. Missing setuid bit?"));
else if (exit_code == 38) // can't get passwd entry for uid 0
e_util_dialog_show(_("Error in Enlightenment System Service"),
_("Enlightenment cannot successfully start<br>"
"the enlightenment_system service<br>"
"because the passwd file entry for root isn't found."));
else if (exit_code == 39) // root has no homedir
e_util_dialog_show(_("Error in Enlightenment System Service"),
_("Enlightenment cannot successfully start<br>"
"the enlightenment_system service<br>"
"because the root home directory is blank."));
else if (exit_code == 40) // root homedir is not a full path
e_util_dialog_show(_("Error in Enlightenment System Service"),
_("Enlightenment cannot successfully start<br>"
"the enlightenment_system service<br>"
"because the root home directory is not a full path."));
else if (exit_code == 41) // root homedir does not resolve to a path
e_util_dialog_show(_("Error in Enlightenment System Service"),
_("Enlightenment cannot successfully start<br>"
"the enlightenment_system service<br>"
"because the root home directory can't be found."));
else if (exit_code == 42) // can't change HOME to root homedir
e_util_dialog_show(_("Error in Enlightenment System Service"),
_("Enlightenment cannot successfully start<br>"
"the enlightenment_system service<br>"
"because it can't change the HOME environment."));
else if (exit_code == 43) // can't change dir to root homedir
e_util_dialog_show(_("Error in Enlightenment System Service"),
_("Enlightenment cannot successfully start<br>"
"the enlightenment_system service<br>"
"because it cannot change working directory to root's home."));
else if (exit_code == 46) // user denied to all subsystems
e_util_dialog_show(_("Error in Enlightenment System Service"),
_("Enlightenment cannot successfully start<br>"
"the enlightenment_system service<br>"
"because your user is denied access to all services.<br>"
"Please see /etc/enlightenment/system.conf."));
else
e_util_dialog_show(_("Error in Enlightenment System Service"),
_("Enlightenment cannot successfully start<br>"
"the enlightenment_system service for<br>"
"some unknown reason."));
return EINA_FALSE;
}
static void
_system_spawn_error(int exit_code)
{
if (_error_dialog_timer) ecore_timer_del(_error_dialog_timer);
_error_dialog_timer = ecore_timer_add(5.0, _cb_dialog_timer,
(void *)(long)exit_code);
}
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(0);
}
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;
head->cmd[23] = 0;
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);
if (list)
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 == 31) || // can't get passwd entry for user
(ev->exit_code == 32) || // username is blank in passwd entry
(ev->exit_code == 33) || // can't alloc mem for username
(ev->exit_code == 34) || // can't get group entry for user's group
(ev->exit_code == 35) || // group name is blank
(ev->exit_code == 36) || // can't setuid to 0
(ev->exit_code == 37) || // can't setgid to 0
(ev->exit_code == 38) || // can't get passwd entry for uid 0
(ev->exit_code == 39) || // root has no homedir
(ev->exit_code == 40) || // root homedir is not a full path
(ev->exit_code == 41) || // root homedir does not resolve to a path
(ev->exit_code == 42) || // can't change HOME to root homedir
(ev->exit_code == 43) || // can't change dir to root homedir
(ev->exit_code == 46) // user denied to all subsystems
)
{
// didn't run because it literally refused....
_system_spawn_error(ev->exit_code);
}
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;
}
}
2020-02-08 01:52:39 -08:00
if (list)
eina_hash_add(_handlers, cmd, list);
}
}