From 7f6e40716bff4e609528c7ba8fd1bf4eb2580615 Mon Sep 17 00:00:00 2001 From: Gavan Fantom Date: Tue, 11 Dec 2007 01:47:01 +0000 Subject: [PATCH] cmdsocket plugin to allow simple commands to be sent over the network --- src/lsc/codegen.c | 3 +- src/lsc/lexer.l | 6 +- src/lsi/Makefile | 6 +- src/lsi/abi.h | 4 + src/lsi/abispec | 4 + src/lsi/cmdsocket.c | 204 ++++++++++++++++++++++++++++++++++++++++++++ src/lsi/cmdsocket.h | 8 ++ src/lsi/mouse.c | 1 + src/lsi/plugins.c | 2 + src/lsi/vm.c | 102 +++++++++++++++++++--- src/lsi/vm.h | 8 +- 11 files changed, 330 insertions(+), 18 deletions(-) create mode 100644 src/lsi/cmdsocket.c create mode 100644 src/lsi/cmdsocket.h diff --git a/src/lsc/codegen.c b/src/lsc/codegen.c index af85d14..44065c6 100644 --- a/src/lsc/codegen.c +++ b/src/lsc/codegen.c @@ -140,7 +140,7 @@ void output_functions_action(struct hashentry *ptr) pad = (4 - (len % 4)) % 4; output_int(len+pad); output_int(0); /* type */ - output_int(get_label(ptr->value) + 8); + output_int(get_label(ptr->value) + 28); output_int(0); /* nargs */ output_string(ptr->name); output_byte(0); /* terminator */ @@ -1267,6 +1267,7 @@ void output_code(void) if (!lookup_constant_string(x, &abiver)) \ printf("%s not defined\n", x); \ output_int(abiver); \ + printf("ABIVERSION(%s) = %x\n", x, abiver); \ } while (0) pc = (pc + 3) & ~3; /* Align table */ diff --git a/src/lsc/lexer.l b/src/lsc/lexer.l index 6322671..4998883 100644 --- a/src/lsc/lexer.l +++ b/src/lsc/lexer.l @@ -105,8 +105,10 @@ return return TOKRETURN; [0-9]+ yylval.Tinteger = atoi(yytext); return NUMBER; -[0-9]+ yylval.Tinteger = atoi(yytext); return NUMBER; -0x[0-9]+ yylval.Tinteger = strtol(yytext+2, (char **)NULL, 16); return NUMBER; --0x[0-9]+ yylval.Tinteger = strtol(yytext+2, (char **)NULL, 16); return NUMBER; + /* Cater for the possibility that things might be signed or + unsigned. This is horrible and ugly, but might just work. */ +0x[0-9a-fA-F]+ yylval.Tinteger = strtoul(yytext+2, (char **)NULL, 16); return NUMBER; +-0x[0-9a-fA-F]+ yylval.Tinteger = strtol(yytext+2, (char **)NULL, 16); return NUMBER; [0-9]+\.[0-9]+ yylval.Treal = atof(yytext); return REAL; -[0-9]+\.[0-9]+ yylval.Treal = atof(yytext); return REAL; [_a-zA-Z0-9]+ yylval.Tstring = strdup(yytext); return IDENTIFIER; diff --git a/src/lsi/Makefile b/src/lsi/Makefile index 7e21f0e..2d54a1c 100644 --- a/src/lsi/Makefile +++ b/src/lsi/Makefile @@ -2,8 +2,10 @@ PREFIX?= /usr/local -OBJS= main.o vm.o plugins.o dmx.o midi.o beatdetect.o fft.o map3d.o mouse.o -SRCS= main.c vm.c plugins.c dmx.c midi.c beatdetect.c fft.c map3d.c mouse.c +OBJS= main.o vm.o plugins.o dmx.o midi.o beatdetect.o fft.o map3d.o mouse.o \ + cmdsocket.o +SRCS= main.c vm.c plugins.c dmx.c midi.c beatdetect.c fft.c map3d.c mouse.c \ + cmdsocket.c OBJS+= abi.o SRCS+= abi.c diff --git a/src/lsi/abi.h b/src/lsi/abi.h index 266b080..5b184f5 100644 --- a/src/lsi/abi.h +++ b/src/lsi/abi.h @@ -44,3 +44,7 @@ int vm_intfn_sin(void); int vm_intfn_cos(void); int vm_intfn_random(void); int vm_intfn_mouse_read(void); +int vm_intfn_cmdsocket_listen(void); +int vm_intfn_cmdsocket_accept(void); +int vm_intfn_cmdsocket_read(void); +int vm_intfn_cmdsocket_prefix(void); diff --git a/src/lsi/abispec b/src/lsi/abispec index 3ac4c24..b002fb4 100644 --- a/src/lsi/abispec +++ b/src/lsi/abispec @@ -33,6 +33,10 @@ function sin function cos function random function mouse_read +function cmdsocket_listen +function cmdsocket_accept +function cmdsocket_read +function cmdsocket_prefix /* * The ABI should be identified by a SHA1 hash of this file diff --git a/src/lsi/cmdsocket.c b/src/lsi/cmdsocket.c new file mode 100644 index 0000000..abaf283 --- /dev/null +++ b/src/lsi/cmdsocket.c @@ -0,0 +1,204 @@ +/* cmdsocket.c */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "vm.h" + +#define PORT 15674 +#define CMD_MAXSIZE 1024 +#define PREFIX_MAXSIZE 1024 + +#define CMDSOCKET_BANNER "lightscript cmdsocket ready\r\n" + +int cmd_sock = 0; +int cmd_active = 0; +int cmd_ignore; +int cmd_bytes; +int cmdsocket_initialised = 0; +char cmd_buf[CMD_MAXSIZE]; +char cmd_prefix[PREFIX_MAXSIZE]; + +int cmdsocket_init(void) +{ + cmdsocket_initialised = 1; + cmd_bytes = 0; + cmd_ignore = 0; + cmd_prefix[0] = '\0'; + + return 1; +} + +void cmdsocket_close(void) +{ + if (cmd_active) + close(cmd_active); + if (cmd_sock) + close(cmd_sock); + cmdsocket_initialised = 0; +} + +void cmdsocket_prefix(char *prefix) +{ + if (!cmdsocket_initialised) + return; + if (strlen(prefix) >= PREFIX_MAXSIZE-1) + return; + strncpy(cmd_prefix, prefix, PREFIX_MAXSIZE); +} + +#define ERROR(x) do { warn((x)); goto error; } while (0) + +#define BACKLOG 0 + +int cmdsocket_listen(int port) { + int sockopt_on = 1; + struct sockaddr_in my_addr; + + cmd_sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); + if (cmd_sock == -1) + ERROR("can't open socket"); + if (setsockopt(cmd_sock, SOL_SOCKET, SO_REUSEADDR, &sockopt_on, + sizeof(int)) == -1) + ERROR("can't set socket options"); + memset((char *) &my_addr, 0, sizeof(my_addr)); + my_addr.sin_family = PF_INET; + my_addr.sin_port = htons(port); + my_addr.sin_addr.s_addr = htonl(INADDR_ANY); + if (bind(cmd_sock, (struct sockaddr *)&my_addr, sizeof(my_addr)) == -1) + ERROR("can't bind socket"); + if (listen(cmd_sock, BACKLOG) == -1) + ERROR("can't listen"); + vm_register_signal_fd(cmd_sock, VM_CMDLISTENQ); + + return 1; + +error: + if (cmd_sock) + close(cmd_sock); + cmd_sock = 0; + return 0; +} + + +int cmdsocket_accept(void) { + struct sockaddr_in client_addr; + socklen_t client_addr_size = sizeof(client_addr); + int c; + + if (!cmdsocket_initialised) + return 0; + if (cmd_sock == 0) + return 0; + + /* We only accept one active connection at a time */ + /* XXX can't just return here */ + if (cmd_active) + return 0; + + c = accept(cmd_sock, (struct sockaddr *)&client_addr, + &client_addr_size); + if (c == -1) { + if (errno == EAGAIN) + return 0; + warn("Error accepting connection"); + close(cmd_sock); + cmd_sock = 0; + return 0; + } + + cmd_active = c; + write(cmd_active, CMDSOCKET_BANNER, sizeof(CMDSOCKET_BANNER)); + + vm_register_signal_fd(cmd_active, VM_CMDREADQ); + vm_wakeup(VM_CMDREADQ); + return 1; +} + +void cmd_parse(char *cmd) { + char *sp; + int arg = 0; + char function[CMD_MAXSIZE + PREFIX_MAXSIZE]; + + *strchr(cmd, '\n') = '\0'; + printf("DEBUG: Received command: %s\n", cmd); + fflush(stdout); + + sp = strtok(cmd, " \t\r"); + sp = strtok(NULL, " \t\r"); + if (sp) { + arg = atoi(sp); + } + + /* cmd_prefix is guaranteed to be terminated */ + strcpy(function, cmd_prefix); + strncat(function, cmd, CMD_MAXSIZE); + + printf("DEBUG: function: %s, arg %d\n", function, arg); + fflush(stdout); + if (vm_spawn_args(function, 1, arg)) { + /* Write an ack here, once a proper function exists */ + } else { + /* Write an error, if it's possible. Don't worry about + missing characters and buffering for now, but we + should eventually */ + write(cmd_active, "ERROR\n", strlen("ERROR\n")); + } + + printf("Received command: %s\n", cmd); +} + +int cmdsocket_read(void) +{ + if (!cmdsocket_initialised) + return 0; + + while (1) { + int rv; + int left = sizeof(cmd_buf) - cmd_bytes; + char *p; + if (!cmd_active) + return 0; + + rv = recv(cmd_active, ((char *)&cmd_buf) + cmd_bytes, left, 0); + if (rv == -1) { + if (errno == EAGAIN) + return 0; + printf("Error reading from socket\n"); + vm_unregister_signal_fd(cmd_active); + close(cmd_active); + cmd_active = 0; + vm_wakeup(VM_CMDLISTENQ); + return 0; + } + if (rv == 0) { + vm_unregister_signal_fd(cmd_active); + close(cmd_active); + cmd_active = 0; + vm_wakeup(VM_CMDLISTENQ); + return 0; + } + cmd_bytes += rv; + if ((p = memchr(cmd_buf, '\n', cmd_bytes))) { + if (!cmd_ignore) + cmd_parse(cmd_buf); + memmove(cmd_buf, p+1, cmd_bytes - (p - cmd_buf - 1)); + cmd_bytes = 0; + cmd_ignore = 0; + } + if (cmd_bytes == sizeof(cmd_buf)) { + cmd_bytes = 0; + cmd_ignore = 1; + /* Overflow */ + } + } +} diff --git a/src/lsi/cmdsocket.h b/src/lsi/cmdsocket.h new file mode 100644 index 0000000..c061d8e --- /dev/null +++ b/src/lsi/cmdsocket.h @@ -0,0 +1,8 @@ +/* cmdsocket.h */ + +int cmdsocket_init(void); +void cmdsocket_close(void); +int cmdsocket_listen(int port); +int cmdsocket_accept(void); +int cmdsocket_read(void); +int cmdsocket_prefix(char *); diff --git a/src/lsi/mouse.c b/src/lsi/mouse.c index 5c3a1be..8a743b0 100644 --- a/src/lsi/mouse.c +++ b/src/lsi/mouse.c @@ -55,6 +55,7 @@ int mouse_init(void) mouse_x = 0; mouse_y = 0; mouse_z = 0; + mouse_bytes = 0; mouse_fd = open(MOUSEDEVICE, O_RDONLY); if (mouse_fd < 0) { diff --git a/src/lsi/plugins.c b/src/lsi/plugins.c index 9f8a5d0..1301165 100644 --- a/src/lsi/plugins.c +++ b/src/lsi/plugins.c @@ -11,12 +11,14 @@ #include "dmx.h" #include "beatdetect.h" #include "mouse.h" +#include "cmdsocket.h" struct plugin plugins_table[] = { {"midi", midi_init, midi_close, 0}, {"dmx", dmx_init, dmx_close, 0}, {"beatdetect", beatdetect_init, beatdetect_close, 0}, {"mouse", mouse_init, mouse_close, 0}, + {"cmdsocket", cmdsocket_init, cmdsocket_close, 0}, }; int nplugins = (sizeof(plugins_table) / sizeof(struct plugin)); diff --git a/src/lsi/vm.c b/src/lsi/vm.c index 7ce47f2..b85a498 100644 --- a/src/lsi/vm.c +++ b/src/lsi/vm.c @@ -11,6 +11,7 @@ #include #include #include +#include #include "vm.h" #include "code.h" @@ -21,6 +22,7 @@ #include "beatdetect.h" #include "map3d.h" #include "mouse.h" +#include "cmdsocket.h" #define DEBUG 0 @@ -41,6 +43,9 @@ struct vm_thread { #define ARRAYBLOCKSIZE 512 +#define SLEEPTIME_SEC 60 +#define SLEEPTIME_NSEC 0 + struct pollfd vm_pollfd[VM_MAXPOLLFD]; int vm_pollfdqueue[VM_MAXPOLLFD]; @@ -55,6 +60,7 @@ struct vm_thread *vm_current = NULL; instr *vm_codearea = NULL; size_t vm_codesize = 0; +int vm_threads = 0; struct hashentry *fnhash[HASHSIZE]; struct hashentry *globhash[HASHSIZE]; @@ -418,6 +424,46 @@ int vm_intfn_mouse_read(void) return 0; } +int vm_intfn_cmdsocket_listen(void) +{ + stack_poke(vm_current, 0, + cmdsocket_listen(stack_get(vm_current, 1))); + return 1; +} + +int vm_intfn_cmdsocket_prefix(void) +{ + char buf[VM_STRING_MAX]; + int len = stack_get(vm_current, 1); + + strncpy(buf, stack_getstr(vm_current, len, 1), len); + buf[len] = '\0'; + + cmdsocket_prefix(buf); + + return 1; +} + +int vm_intfn_cmdsocket_accept(void) +{ + if (!cmdsocket_accept()) { + vm_queue(vm_current, VM_CMDLISTENQ); + vm_current = NULL; + return 0; + } + return 1; +} + +int vm_intfn_cmdsocket_read(void) +{ + if (!cmdsocket_read()) { + vm_queue(vm_current, VM_CMDREADQ); + vm_current = NULL; + return 0; + } + return 1; +} + int vm_intfn_beatdetect_read(void) { if (!beatdetect_read()) { @@ -570,6 +616,7 @@ void vm_init(void) vm_npollfds = 0; vm_caughtsignal = 0; signal(SIGIO, vm_sighandler); + signal(SIGPIPE, SIG_IGN); } void vm_load_file(char *filename) @@ -673,10 +720,11 @@ void vm_queue(struct vm_thread *thread, int queue) vm_queues[queue] = thread; } -int vm_spawn(char *fn) +int vm_spawn_args(char *fn, int n, ...) { struct vm_thread *newt; struct hashentry *ptr; + va_list ap; ptr = hash_lookup(fnhash, fn, 0); if (ptr == NULL) { @@ -705,6 +753,12 @@ int vm_spawn(char *fn) /* Push return address here, to point to some special thread exit routine */ + /* Push optional arguments */ + va_start(ap, n); + while (n--) + stack_push(newt, va_arg(ap, int)); + va_end(ap); + stack_push(newt, 0); /* Return value */ stack_push(newt, 0); /* Return address */ @@ -712,15 +766,26 @@ int vm_spawn(char *fn) newt->prev = NULL; newt->queue = VM_NOQUEUE; vm_queue(newt, VM_RUNQ); + vm_threads++; return 1; } +int vm_spawn(char *fn) +{ + return vm_spawn_args(fn, 0); +} + void vm_destroy(struct vm_thread *thread) { vm_unqueue(thread); free(thread->stackbase); free(thread); + vm_threads--; + if (vm_threads == 0) { + printf("No threads left\n"); + exit(0); + } } int vm_runnable(struct vm_thread *thread) @@ -757,18 +822,19 @@ void vm_sched(void) struct timespec ts; int rv; - if (vm_queues[VM_TIMEQ] == NULL) { - printf("No runnable thread, and no waiting thread\n"); - exit(0); - } // printf("No runnable thread - sleeping\n"); - gettimeofday(&tv, NULL); - timersub(&vm_queues[VM_TIMEQ]->time, &tv, &tv); - if ((tv.tv_sec < 0) || (tv.tv_usec < 0)) { - tv.tv_sec = 0; - tv.tv_usec = 0; + if (vm_queues[VM_TIMEQ]) { + gettimeofday(&tv, NULL); + timersub(&vm_queues[VM_TIMEQ]->time, &tv, &tv); + if ((tv.tv_sec < 0) || (tv.tv_usec < 0)) { + tv.tv_sec = 0; + tv.tv_usec = 0; + } + TIMEVAL_TO_TIMESPEC(&tv, &ts); + } else { + ts.tv_sec = SLEEPTIME_SEC; + ts.tv_nsec = SLEEPTIME_NSEC; } - TIMEVAL_TO_TIMESPEC(&tv, &ts); // nanosleep(&ts, NULL); rv = pollts(vm_pollfd, vm_npollfds, &ts, NULL); if ((rv == -1) && (errno != EINTR)) @@ -791,6 +857,20 @@ void vm_register_signal_fd(int fd, int queue) rv = fcntl(fd, F_SETFL, O_NONBLOCK | O_ASYNC); } +void vm_unregister_signal_fd(int fd) +{ + int i; + + for (i = 0; i < vm_npollfds; i++) { + if (fd == vm_pollfd[i].fd) { + memmove(&vm_pollfd[i], &vm_pollfd[i+1], + sizeof(struct pollfd) * (vm_npollfds-i-1)); + vm_npollfds--; + return; + } + } +} + void stack_push(struct vm_thread *thread, stkentry e) { thread->sp++; diff --git a/src/lsi/vm.h b/src/lsi/vm.h index 2700511..3494097 100644 --- a/src/lsi/vm.h +++ b/src/lsi/vm.h @@ -3,8 +3,11 @@ void vm_init(void); void vm_load(char *); int vm_spawn(char *); +int vm_spawn_args(char *, int, ...); void vm_run(void); void vm_register_signal_fd(int /* fd */, int /* queue */); +void vm_unregister_signal_fd(int /* fd */); +void vm_wakeup(int queue); #define VM_MAXQUEUES 512 #define VM_RUNQ 0 @@ -13,8 +16,9 @@ void vm_register_signal_fd(int /* fd */, int /* queue */); #define VM_MOUSEQ 6 #define VM_BEATQ 7 #define VM_MIDIQ 8 -#define VM_SOCKQ 9 +#define VM_CMDLISTENQ 9 +#define VM_CMDREADQ 10 -#define VM_USERQMIN 10 +#define VM_USERQMIN 20 #define VM_NOQUEUE (-1)