From 5d251856846fdf6b40d25b0ca2e278c399d60059 Mon Sep 17 00:00:00 2001 From: martii Date: Mon, 13 Oct 2014 22:11:25 +0200 Subject: [PATCH] lua: implement "luaclient". This allows for starting Lua scripts in neutrino context from the command line. Experimental, not fully regression-tested. --- acinclude.m4 | 4 + configure.ac | 1 + lib/Makefile.am | 3 +- lib/luaclient/Makefile.am | 13 +++ lib/luaclient/luaclient.cpp | 110 +++++++++++++++++++++++ lib/luaclient/luaclient.h | 25 ++++++ src/gui/luainstance.cpp | 174 +++++++++++++++++++++++++++++++++++- src/neutrino.cpp | 22 +++++ src/neutrino.h | 4 + 9 files changed, 354 insertions(+), 2 deletions(-) create mode 100644 lib/luaclient/Makefile.am create mode 100644 lib/luaclient/luaclient.cpp create mode 100644 lib/luaclient/luaclient.h diff --git a/acinclude.m4 b/acinclude.m4 index f8a67caec..7e48ea787 100644 --- a/acinclude.m4 +++ b/acinclude.m4 @@ -140,6 +140,9 @@ TUXBOX_APPS_DIRECTORY_ONE(libdir,LIBDIR,libdir,/lib,/tuxbox, TUXBOX_APPS_DIRECTORY_ONE(plugindir,PLUGINDIR,libdir,/lib,/tuxbox/plugins, [--with-plugindir=PATH ],[where to find the plugins]) +TUXBOX_APPS_DIRECTORY_ONE(luaplugindir,LUAPLUGINDIR,libdir,/lib,/tuxbox/luaplugins, + [--with-luaplugindir=PATH ],[where to find Lua plugins]) + TUXBOX_APPS_DIRECTORY_ONE(ucodedir,UCODEDIR,localstatedir,/var,/tuxbox/ucodes, [--with-ucodedir=PATH ],[where to find the ucodes]) @@ -167,6 +170,7 @@ AC_SUBST(GAMESDIR) AC_SUBST(LIBDIR) AC_SUBST(MNTDIR) AC_SUBST(PLUGINDIR) +AC_SUBST(LUAPLUGINDIR) AC_SUBST(UCODEDIR) AC_SUBST(THEMESDIR) AC_SUBST(ICONSDIR) diff --git a/configure.ac b/configure.ac index d113ada59..dd672547f 100644 --- a/configure.ac +++ b/configure.ac @@ -246,6 +246,7 @@ lib/libtuxtxt/Makefile lib/libdvbsub/Makefile lib/libupnpclient/Makefile lib/libiw/Makefile +lib/luaclient/Makefile src/lcddisplay/Makefile src/nhttpd/Makefile src/nhttpd/web/Makefile diff --git a/lib/Makefile.am b/lib/Makefile.am index 089a06010..8aa3ac8ab 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -9,7 +9,8 @@ SUBDIRS = \ xmltree \ libtuxtxt \ libiw \ - libdvbsub + libdvbsub \ + luaclient if ENABLE_UPNP SUBDIRS += \ diff --git a/lib/luaclient/Makefile.am b/lib/luaclient/Makefile.am new file mode 100644 index 000000000..0a07a056a --- /dev/null +++ b/lib/luaclient/Makefile.am @@ -0,0 +1,13 @@ +AM_CPPFLAGS = \ + -I$(top_srcdir)/lib \ + -I$(top_srcdir)/src \ + -I$(top_srcdir)/lib/luaclient \ + -I$(top_srcdir)/lib/connection + +AM_CPPFLAGS += -fno-rtti -fno-exceptions + +bin_PROGRAMS = luaclient + +luaclient_SOURCES = luaclient.cpp +luaclient_LDADD = \ + $(top_builddir)/lib/connection/libtuxbox-connection.a diff --git a/lib/luaclient/luaclient.cpp b/lib/luaclient/luaclient.cpp new file mode 100644 index 000000000..7344dcad6 --- /dev/null +++ b/lib/luaclient/luaclient.cpp @@ -0,0 +1,110 @@ +/* + (C)2014 by martii + + License: GPL + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include +#include +#include +#include +#include + +#include + + +class CLuaClient : private CBasicClient +{ + private: + unsigned char getVersion () const { return LUACLIENT_VERSION; }; + const char * getSocketName() const { return LUACLIENT_UDS_NAME; }; + public: + bool Send(const char *data, const size_t size) { return send(0, data, size);} + bool Recv(char *data, const size_t size) { return receive_data(data, size, true); } + ~CLuaClient() { close_connection(); } +}; + +int main(int argc, char** argv) +{ + char *cmd = strrchr(argv[0], '/'); + if (cmd) + cmd++; + else + cmd = argv[0]; + if (!strcmp(cmd, "luaclient")) + argv++, argc--; + + if (!*argv) { + fprintf(stderr, + "Usage: luaclient [command [arguments ...]]\n" + " or: command [arguments ...] (with command being a link to luaclient)\n"); + exit(-1); + } + + size_t len[argc]; + size_t size = 0; + for (int i = 0; i < argc; i++) { + len[i] = strlen(argv[i]) + 1; + size += len[i]; + } + + char data[size + sizeof(size)]; + char *b = data; + memcpy(b, &size, sizeof(size)); + size += sizeof(size); + b += sizeof(size); + for (int i = 0; i < argc; i++) { + memcpy(b, argv[i], len[i]); + b += len[i]; + } + + CLuaClient client; + int res = -1; + const char *fun = NULL; + char *resp = NULL; + + if (!client.Send(data, size)) { + fun = "Send failed"; + goto fail; + } + if (!client.Recv((char *)&size, sizeof(size))) { + fun = "Recv (1) failed"; + goto fail; + } + char result[size]; + if (!client.Recv(result, size)) { + fun = "Recv (2) failed"; + goto fail; + } + + if (result[size - 1]) { + fun = "unterminated result"; + goto fail; + } + res = atoi(result); + resp = result + strlen(result) + 1; + if (resp < result + size) + printf("%s", resp); + resp += strlen(resp) + 1; + if (resp < result + size) + fprintf(stderr, "%s", resp); + exit(res); +fail: + if (fun) + fprintf(stderr, "luaclient: %s.\n", fun); + exit(-1); +} diff --git a/lib/luaclient/luaclient.h b/lib/luaclient/luaclient.h new file mode 100644 index 000000000..20ca3acb9 --- /dev/null +++ b/lib/luaclient/luaclient.h @@ -0,0 +1,25 @@ +/* + (C)2014 by martii + + License: GPL + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifndef __LUACLIENT__H__ +#define __LUACLIENT__H__ +#define LUACLIENT_UDS_NAME "/tmp/luaclient.sock" +#define LUACLIENT_VERSION 1 +#endif diff --git a/src/gui/luainstance.cpp b/src/gui/luainstance.cpp index e37527a25..315e2cbff 100644 --- a/src/gui/luainstance.cpp +++ b/src/gui/luainstance.cpp @@ -24,15 +24,20 @@ #include #include +#include #include #include +#include #include #include #include #include #include #include -#include +#include +#include +#include + #include "luainstance.h" #include @@ -2471,3 +2476,170 @@ int CLuaInstance::LuaConfigFileDelete(lua_State *L) } // -------------------------------------------------------------------------------- + +#if 0 +For testing try: + +cat < /lib/tuxbox/luaplugins/test.lua +for i,v in ipairs(arg) do + print(tostring(i) .. "\t" .. tostring(v)) +end +return "ok" +EOT + +followed by luaclient test a b c d +#endif +class luaserver_data +{ + public: + int fd; + std::vector argv; + std::string script; + + luaserver_data(int _fd, std::string &_script) { + fd = dup(_fd); + fcntl(fd, F_SETFD, FD_CLOEXEC); + script = _script; + } + ~luaserver_data(void) { + close(fd); + } + +}; + +static int luaserver_count = 0; +static pthread_mutex_t luaserver_mutex; + +static void luaserver_lock(void) +{ + pthread_mutex_lock(&luaserver_mutex); +} + +static void luaserver_unlock(void) +{ + pthread_mutex_unlock(&luaserver_mutex); +} + +static void *luaserver_thread(void *arg) { + set_threadname(__func__); + luaserver_lock(); + luaserver_count++; + luaserver_unlock(); + sem_post(&CNeutrinoApp::getInstance()->lua_did_run); + + luaserver_data *lsd = (class luaserver_data *)arg; + + CLuaInstance lua; + std::string result_code; + std::string result_string; + std::string error_string; + lua.runScript(lsd->script.c_str(), &lsd->argv, &result_code, &result_string, &error_string); + size_t result_code_len = result_code.length() + 1; + size_t result_string_len = result_string.length() + 1; + size_t error_string_len = error_string.length() + 1; + size_t size = result_code_len + result_string_len + error_string_len; + char result[size + sizeof(size)]; + char *rp = result; + memcpy(rp, &size, sizeof(size)); + rp += sizeof(size); + size += sizeof(size); + memcpy(rp, result_code.c_str(), result_code_len); + rp += result_code_len; + memcpy(rp, result_string.c_str(), result_string_len); + rp += result_string_len; + memcpy(rp, error_string.c_str(), error_string_len); + rp += error_string_len; + CBasicServer::send_data(lsd->fd, result, size); + + delete lsd; + luaserver_lock(); + luaserver_count--; + if (!luaserver_count) + sem_post(&CNeutrinoApp::getInstance()->lua_may_run); + luaserver_unlock(); + pthread_exit(NULL); +} + +static bool luaserver_parse_command(CBasicMessage::Header &rmsg __attribute__((unused)), int connfd) +{ + size_t size; + + if (!CBasicServer::receive_data(connfd, &size, sizeof(size))) { + fprintf(stderr, "%s %s %d: receive_data failed\n", __FILE__, __func__, __LINE__); + return true; + } + char data[size]; + if (!CBasicServer::receive_data(connfd, data, size)) { + fprintf(stderr, "%s %s %d: receive_data failed\n", __FILE__, __func__, __LINE__); + return true; + } + if (data[size - 1]) { + fprintf(stderr, "%s %s %d: unterminated string\n", __FILE__, __func__, __LINE__); + return true; + } + std::string luascript(LUAPLUGINDIR "/"); + luascript += data; + luascript += ".lua"; + if (access(luascript, R_OK)) { + fprintf(stderr, "%s %s %d: %s not found\n", __FILE__, __func__, __LINE__, luascript.c_str()); + const char *result_code = "-1"; + const char *result_string = ""; + std::string error_string = luascript + " not found\n"; + size_t result_code_len = strlen(result_code) + 1; + size_t result_string_len = strlen(result_string) + 1; + size_t error_string_len = strlen(error_string.c_str()) + 1; + size = result_code_len + result_string_len + error_string_len; + char result[size + sizeof(size)]; + char *rp = result; + memcpy(rp, &size, sizeof(size)); + rp += sizeof(size); + size += sizeof(size); + memcpy(rp, result_code, result_code_len); + rp += result_code_len; + memcpy(rp, result_string, result_string_len); + rp += result_string_len; + memcpy(rp, error_string.c_str(), error_string_len); + rp += error_string_len; + CBasicServer::send_data(connfd, result, size); + return true; + } + luaserver_data *lsd = new luaserver_data(connfd, luascript); + char *datap = data; + while (size > 0) { + lsd->argv.push_back(std::string(datap)); + size_t len = strlen(datap) + 1; + datap += len; + size -= len; + } + + luaserver_lock(); + if (!luaserver_count) + sem_wait(&CNeutrinoApp::getInstance()->lua_may_run); + luaserver_unlock(); + + pthread_t thr; + pthread_create (&thr, NULL, luaserver_thread, (void *) lsd); + pthread_detach(thr); + + return true; +} + +void *luaserver_main_thread(void *) { + set_threadname(__func__); + + CBasicServer server; + if (!server.prepare(LUACLIENT_UDS_NAME)) { + fprintf(stderr, "%s %s %d: prepare failed\n", __FILE__, __func__, __LINE__); + pthread_exit(NULL); + } + + pthread_mutexattr_t attr; + pthread_mutexattr_init(&attr); + pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK_NP); + pthread_mutex_init(&luaserver_mutex, &attr); + + server.run(luaserver_parse_command, LUACLIENT_VERSION); + pthread_exit(NULL); +} + +// -------------------------------------------------------------------------------- diff --git a/src/neutrino.cpp b/src/neutrino.cpp index a1317b0f3..514d03188 100644 --- a/src/neutrino.cpp +++ b/src/neutrino.cpp @@ -149,6 +149,8 @@ static bool timerd_thread_started = false; void * nhttpd_main_thread(void *data); +void * luaserver_main_thread(void *data); + //#define DISABLE_SECTIONSD extern cVideo * videoDecoder; @@ -1947,6 +1949,10 @@ TIMER_START(); if (!pthread_create (&nhttpd_thread, NULL, nhttpd_main_thread, (void *) NULL)) pthread_detach (nhttpd_thread); + pthread_t luaserver_thread; + pthread_create (&luaserver_thread, NULL, luaserver_main_thread, (void *) NULL); + pthread_detach(luaserver_thread); + CStreamManager::getInstance()->Start(); #ifndef DISABLE_SECTIONSD @@ -2106,8 +2112,19 @@ void CNeutrinoApp::RealRun(CMenuWidget &mainMenu) //cCA::GetInstance()->Ready(true); + sem_init(&lua_may_run, 0, 0); + sem_init(&lua_did_run, 0, 0); + while( true ) { + sem_post(&lua_may_run); g_RCInput->getMsg(&msg, &data, 100, ((g_settings.mode_left_right_key_tv == SNeutrinoSettings::VOLUME) && (g_RemoteControl->subChannels.size() < 1)) ? true : false); // 10 secs.. + sem_wait(&lua_may_run); + if (!sem_trywait(&lua_did_run)) { + if (msg != CRCInput::RC_timeout) + g_RCInput->postMsg(msg, data); + while (!sem_trywait(&lua_did_run)); + continue; + } if( ( mode == mode_tv ) || ( mode == mode_radio ) || ( mode == mode_webtv ) ) { if( (msg == NeutrinoMessages::SHOW_EPG) /* || (msg == CRCInput::RC_info) */ ) { @@ -4295,6 +4312,11 @@ void CNeutrinoApp::Cleanup() delete CEitManager::getInstance(); printf("cleanup 6\n");fflush(stdout); delete CVFD::getInstance(); + + // FIXME -- wait for luaserver threads to terminate? + sem_destroy(&lua_may_run); + sem_destroy(&lua_did_run); + #ifdef __UCLIBC__ malloc_stats(NULL); #else diff --git a/src/neutrino.h b/src/neutrino.h index 70c59772a..82901f321 100644 --- a/src/neutrino.h +++ b/src/neutrino.h @@ -34,6 +34,7 @@ #define __neutrino__ #include +#include #include #include "driver/framebuffer.h" @@ -172,6 +173,9 @@ public: CChannelList *RADIOchannelList; CChannelList *channelList; + sem_t lua_may_run; + sem_t lua_did_run; + static CNeutrinoApp* getInstance(); void channelsInit(bool bOnly = false);