efl/src/bin/ecore_con/efl_net_proxy_helper.c

261 lines
6.2 KiB
C
Raw Normal View History

ecore_con - move libproxy to a slave binary with stdin/out msging so here's the ugly problem. libproxy. yes. we've discussed memory usage (e.g. it may have to execute javascript and pull in lots of deps etc.) but we dlopene'd on the fly. ok... but this didn't solve another issue i hit: libproxy was causing enlightenment to abort(). some internal bit of libproxy was raising a c++ exception. this wasn't caught. this causes an abort(). takes down your entire desktop. FANTASTIC. this is bad. i wouldnt' expect a library we depend on to be THIS anti-social but libproxy seemingly is. it SHOULd catch its error sand just propagate back to us so we can handle gracefully. there reall is no way around this - isolate libproxy. it's even worse that libproxy can load arbitrary modules that come from anywhere sho who knows what issues this can cause. isolation is the best solution i can think of. so this makes an elf+net_proxy_helper we spawn the first time we need a proxy lookup. we re-use that binary again and again until it exits (it should exit after 10 seconds of being idle with no requests coming in/pending). it'll respawn again later if needed. this involves now the efl net threads having to marshall back to mainloop to do the spawn and to write to the proxy process (reading is done by async exe data events and the data is passed down a thread queue to the waitng efl net thread). if the exe dies with pending requests unanswered then it's respawned again and the req's are re-sent to it... just in case. it has a limit on how often it'll respawn quickly. this seems to work in my limited testing. this ALSO now isolates memory usage of libproxy to another slave process AND this process will die taking its memory with it once it's been idle for long enough. that;s also another good solution to keeping libproxy impact at bay.
2017-01-08 05:57:54 -08:00
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
#include "Eina.h"
#ifdef ERR
# undef ERR
#endif
#define ERR(...) EINA_LOG_DOM_ERR(EINA_LOG_DOMAIN_GLOBAL, __VA_ARGS__)
#ifdef DBG
# undef DBG
#endif
#define DBG(...) EINA_LOG_DOM_DBG(EINA_LOG_DOMAIN_GLOBAL, __VA_ARGS__)
#ifdef INF
# undef INF
#endif
#define INF(...) EINA_LOG_DOM_INFO(EINA_LOG_DOMAIN_GLOBAL, __VA_ARGS__)
#ifdef WRN
# undef WRN
#endif
#define WRN(...) EINA_LOG_DOM_WARN(EINA_LOG_DOMAIN_GLOBAL, __VA_ARGS__)
#ifdef CRI
# undef CRI
#endif
#define CRI(...) EINA_LOG_DOM_CRIT(EINA_LOG_DOMAIN_GLOBAL, __VA_ARGS__)
typedef struct pxProxyFactory_ pxProxyFactory;
typedef struct _Libproxy
{
pxProxyFactory *factory;
char **(*px_proxy_factory_get_proxies) (pxProxyFactory *factory, const char *url);
void *(*px_proxy_factory_new) (void);
void (*px_proxy_factory_free) (pxProxyFactory *);
Eina_Module *mod;
} Libproxy;
static Libproxy _libproxy = { 0 };
static Eina_Spinlock pending_lock;
static int pending = 0;
static int opcount = 0;
static Eina_List *join_list = NULL;
static Eina_Bool
init(void)
{
if (!_libproxy.mod)
{
#define LOAD(x) \
if (!_libproxy.mod) { \
_libproxy.mod = eina_module_new(x); \
if (_libproxy.mod) { \
if (!eina_module_load(_libproxy.mod)) { \
eina_module_free(_libproxy.mod); \
_libproxy.mod = NULL; \
} \
} \
}
#if defined(_WIN32) || defined(__CYGWIN__)
LOAD("libproxy-1.dll");
LOAD("libproxy.dll");
#elif defined(__APPLE__) && defined(__MACH__)
LOAD("libproxy.1.dylib");
LOAD("libproxy.dylib");
#else
LOAD("libproxy.so.1");
LOAD("libproxy.so");
#endif
#undef LOAD
if (!_libproxy.mod)
{
DBG("Couldn't find libproxy in your system. Continue without it");
return EINA_FALSE;
}
#define SYM(x) \
if ((_libproxy.x = eina_module_symbol_get(_libproxy.mod, #x)) == NULL) { \
ERR("libproxy (%s) missing symbol %s", \
eina_module_file_get(_libproxy.mod), #x); \
eina_module_free(_libproxy.mod); \
_libproxy.mod = NULL; \
return EINA_FALSE; \
}
SYM(px_proxy_factory_new);
SYM(px_proxy_factory_free);
SYM(px_proxy_factory_get_proxies);
#undef SYM
DBG("using libproxy=%s", eina_module_file_get(_libproxy.mod));
}
if (!_libproxy.factory)
_libproxy.factory = _libproxy.px_proxy_factory_new();
return !!_libproxy.factory;
}
static void
shutdown(void)
{
if (_libproxy.factory)
{
_libproxy.px_proxy_factory_free(_libproxy.factory);
_libproxy.factory = NULL;
}
if (_libproxy.mod)
{
eina_module_free(_libproxy.mod);
_libproxy.mod = NULL;
}
}
static void *
proxy_lookup(void *data, Eina_Thread t)
{
char *cmd = data;
char **proxies, **itr;
const char *p, *url;
int id = atoi(cmd + 2);
int pending_local, opcount_prev;
if (id > 0)
{
for (p = cmd + 2; *p && (*p != ' '); p++);
if (*p == ' ')
{
url = p + 1;
proxies = _libproxy.px_proxy_factory_get_proxies
(_libproxy.factory, url);
if (proxies)
{
for (itr = proxies; *itr != NULL; itr++)
{
fprintf(stdout, "P %i P %s\n", id, *itr);
free(*itr);
}
free(proxies);
}
fprintf(stdout, "P %i E\n", id);
fflush(stdout);
}
}
free(cmd);
eina_spinlock_take(&pending_lock);
{
pending--;
pending_local = pending;
opcount_prev = opcount;
}
eina_spinlock_release(&pending_lock);
// if there are no more pending threads doing work - sleep for the
// timeout then check if we still are and if so - exit;
if (pending_local == 0) sleep(10);
eina_spinlock_take(&pending_lock);
{
Eina_Thread *tt;
if ((pending == 0) & (opcount == opcount_prev)) exit(0);
tt = calloc(1, sizeof(Eina_Thread));
if (tt)
{
*tt = t;
join_list = eina_list_append(join_list, tt);
}
}
eina_spinlock_release(&pending_lock);
return NULL;
}
static void
handle(const char *cmd)
{
// "P 1234 URL" -> Get Proxy, id=1234, url=URL
if ((cmd[0] == 'P') && (cmd[1] == ' '))
{
char *dup = strdup(cmd);
if (dup)
{
Eina_Thread t;
eina_spinlock_take(&pending_lock);
{
pending++;
opcount++;
}
eina_spinlock_release(&pending_lock);
if (!eina_thread_create(&t, EINA_THREAD_BACKGROUND, -1,
proxy_lookup, dup))
{
abort();
}
}
return;
}
}
static void
clean_threads(void)
{
eina_spinlock_take(&pending_lock);
{
Eina_Thread *t;
EINA_LIST_FREE(join_list, t)
{
eina_thread_join(*t);
free(t);
}
}
eina_spinlock_release(&pending_lock);
}
int
main(int argc EINA_UNUSED, char **argv EINA_UNUSED)
{
char inbuf[8192];
eina_init();
if (init())
{
eina_spinlock_new(&pending_lock);
// 1 command per stdin line
while (fgets(inbuf, sizeof(inbuf) - 1, stdin))
{
// strip off newline and ensure the string is 0 terminated
int len = strlen(inbuf);
if (len > 0)
{
if (inbuf[len -1 ] == '\n') inbuf[len - 1] = 0;
else inbuf[len] = 0;
handle(inbuf);
}
clean_threads();
}
eina_spinlock_free(&pending_lock);
shutdown();
}
else
{
// Failed to init libproxy so report this before exit
fprintf(stdout, "F\n");
fflush(stdout);
for (;;) sleep(60 * 60 * 24);
ecore_con - move libproxy to a slave binary with stdin/out msging so here's the ugly problem. libproxy. yes. we've discussed memory usage (e.g. it may have to execute javascript and pull in lots of deps etc.) but we dlopene'd on the fly. ok... but this didn't solve another issue i hit: libproxy was causing enlightenment to abort(). some internal bit of libproxy was raising a c++ exception. this wasn't caught. this causes an abort(). takes down your entire desktop. FANTASTIC. this is bad. i wouldnt' expect a library we depend on to be THIS anti-social but libproxy seemingly is. it SHOULd catch its error sand just propagate back to us so we can handle gracefully. there reall is no way around this - isolate libproxy. it's even worse that libproxy can load arbitrary modules that come from anywhere sho who knows what issues this can cause. isolation is the best solution i can think of. so this makes an elf+net_proxy_helper we spawn the first time we need a proxy lookup. we re-use that binary again and again until it exits (it should exit after 10 seconds of being idle with no requests coming in/pending). it'll respawn again later if needed. this involves now the efl net threads having to marshall back to mainloop to do the spawn and to write to the proxy process (reading is done by async exe data events and the data is passed down a thread queue to the waitng efl net thread). if the exe dies with pending requests unanswered then it's respawned again and the req's are re-sent to it... just in case. it has a limit on how often it'll respawn quickly. this seems to work in my limited testing. this ALSO now isolates memory usage of libproxy to another slave process AND this process will die taking its memory with it once it's been idle for long enough. that;s also another good solution to keeping libproxy impact at bay.
2017-01-08 05:57:54 -08:00
}
eina_shutdown();
return 0;
}