diff options
author | Christian Pointner <equinox@helsinki.at> | 2011-03-30 01:13:17 (GMT) |
---|---|---|
committer | Christian Pointner <equinox@helsinki.at> | 2011-03-30 01:13:17 (GMT) |
commit | a4f9323b1550715446453c6015a4e2df48f98855 (patch) | |
tree | 21c4e2e21dfc5c7fee3a68c4db2a833d777961ea /rhnop-server | |
parent | 3af25a5734457ff5aca954b2452cd32d274dbd26 (diff) |
moved to new names (rhnop-client and server)
Diffstat (limited to 'rhnop-server')
-rw-r--r-- | rhnop-server/Makefile | 91 | ||||
-rw-r--r-- | rhnop-server/conf.lua | 33 | ||||
-rwxr-xr-x | rhnop-server/configure | 190 | ||||
-rw-r--r-- | rhnop-server/l_pipe.c | 137 | ||||
-rw-r--r-- | rhnop-server/l_pipe.h | 33 | ||||
-rwxr-xr-x | rhnop-server/noprml | 59 | ||||
-rw-r--r-- | rhnop-server/nopsyncd.c | 165 | ||||
-rw-r--r-- | rhnop-server/nopsyncd.conf | 15 | ||||
-rw-r--r-- | rhnop-server/playlog.lua | 90 | ||||
-rwxr-xr-x | rhnop-server/qlistener.lua | 89 | ||||
-rw-r--r-- | rhnop-server/rddb.lua | 75 | ||||
-rwxr-xr-x | rhnop-server/tcpserver.lua | 128 |
12 files changed, 1105 insertions, 0 deletions
diff --git a/rhnop-server/Makefile b/rhnop-server/Makefile new file mode 100644 index 0000000..a540d77 --- /dev/null +++ b/rhnop-server/Makefile @@ -0,0 +1,91 @@ +## +## rhnop +## +## Copyright (C) 2011 Christian Pointner <equinox@helsinki.at> +## +## This file is part of rhnop. +## +## rhnop 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 3 of the License, or +## any later version. +## +## rhnop 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 rhnop. If not, see <http://www.gnu.org/licenses/>. +## + +ifneq ($(MAKECMDGOALS),distclean) +include include.mk +endif + +EXECUTABLE := nopsyncd + +C_OBJS := l_pipe.o \ + nopsyncd.o + +C_SRCS := $(C_OBJS:%.o=%.c) + +.PHONY: clean distclean install install-bin install-etc uninstall remove purge + +all: $(EXECUTABLE) + +%.d: %.c + @set -e; rm -f $@; \ + $(CC) -MM $(CFLAGS) $< > $@.$$$$; \ + sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \ + rm -f $@.$$$$; echo '(re)building $@' + +ifneq ($(MAKECMDGOALS),distclean) +-include $(C_SRCS:%.c=%.d) +endif + +$(EXECUTABLE): $(C_OBJS) + $(CC) $(C_OBJS) -o $@ $(LDFLAGS) + +%.o: %.c + $(CC) $(CFLAGS) -c $< + +strip: $(EXECUTABLE) + $(STRIP) -s $(EXECUTABLE) + + +distclean: clean + find . -name *.o -exec rm -f {} \; + find . -name "*.\~*" -exec rm -rf {} \; + rm -f include.mk + +clean: + rm -f *.o + rm -f *.d + rm -f *.d.* + rm -f $(EXECUTABLE) + +INSTALL_TARGETS := install-bin install-etc +REMOVE_TARGETS := remove-bin remove-etc + +install: all $(INSTALL_TARGETS) + +install-bin: $(EXECUTABLE) + $(INSTALL) -d $(DESTDIR)$(BINDIR) + $(INSTALL) -m 755 $(EXECUTABLE) $(DESTDIR)$(BINDIR) + +install-etc: + $(INSTALL) -d $(DESTDIR)$(ETCDIR)/$(EXECUTABLE) + +uninstall: remove + +remove: $(REMOVE_TARGETS) + +remove-bin: + rm -f $(DESTDIR)$(BINDIR)/$(EXECUTABLE) + +remove-etc: + rm -f $(DESTDIR)$(ETCDIR)/init.d/$(EXECUTABLE) + +purge: remove + rm -rf $(DESTDIR)$(ETCDIR)/$(EXECUTABLE)/ diff --git a/rhnop-server/conf.lua b/rhnop-server/conf.lua new file mode 100644 index 0000000..83257b5 --- /dev/null +++ b/rhnop-server/conf.lua @@ -0,0 +1,33 @@ +-- +-- rhnop +-- +-- Copyright (C) 2011 Christian Pointner <equinox@helsinki.at> +-- +-- This file is part of rhnop. +-- +-- rhnop 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 3 of the License, or +-- any later version. +-- +-- rhnop 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 rhnop. If not, see <http://www.gnu.org/licenses/>. +-- + +local conf = {} + +local file = assert(io.open("nopsyncd.conf", "r")) +for line in file:lines() do + local k,v = string.match(line, "^([^=#]+)=(.*)$") + if k and v and v ~= "" then + conf[k] = v + end +end +file:close() + +return conf diff --git a/rhnop-server/configure b/rhnop-server/configure new file mode 100755 index 0000000..30145f2 --- /dev/null +++ b/rhnop-server/configure @@ -0,0 +1,190 @@ +#!/bin/sh +# +# rhnop +# +# Copyright (C) 2011 Christian Pointner <equinox@helsinki.at> +# +# This file is part of rhnop. +# +# rhnop 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 3 of the License, or +# any later version. +# +# rhnop 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 rhnop. If not, see <http://www.gnu.org/licenses/>. +# + +TARGET=`uname -s` +EBUILD_COMPAT=0 + +CFLAGS='-g -O2' +LDFLAGS='-g -Wall -O2 -lpthread' + +LUA_DIR='' +LUA='' + +PREFIX='/usr/local' +BINDIR='' +ETCDIR='' + +print_usage() { + echo "configure --help print this" + echo " --target=<TARGET> build target i.e. Linux (default: autodetect)" + echo " --prefix=<PREFIX> the installation prefix (default: /usr/local)" + echo " --bindir=<DIR> the path to the bin directory (default: $PREFIX/bin)" + echo " --sysconfdir=<DIR> the path to the system configuration directory (default: $PREFIX/etc)" + echo " --with-lua=<DIR> use this lua tree instead of system default" +} + +for arg +do + case $arg in + --target=*) + TARGET=${arg#--target=} + ;; + --prefix=*) + PREFIX=${arg#--prefix=} + ;; + --bindir=*) + BINDIR=${arg#--bindir=} + ;; + --sysconfdir=*) + ETCDIR=${arg#--sysconfdir=} + ;; + --with-lua=*) + LUA_DIR=${arg#--with-lua=} + ;; + --ebuild-compat) + EBUILD_COMPAT=1 + ;; + --help) + print_usage + exit 0 + ;; + *) + ERRORS="$ERRORS $arg" + ;; + esac +done + +if [ -n "$ERRORS" ] && [ $EBUILD_COMPAT -ne 1 ]; then + for error in $ERRORS; do + echo "Unknown argument: $error" + done + + print_usage + exit 1 +fi + +rm -f include.mk +case $TARGET in + Linux) + LDFLAGS=$LDFLAGS' -ldl' + ;; + *) + echo "platform not supported" + exit 1; + ;; +esac + + +test_lua_version() +{ + LUA_VERSION=`cat $1 | grep "#define LUA_VERSION[ ]" | cut -f2- | tr -d '"' | sed -e 's/Lua \([0-9][0-9.]*\)/\1/'` + LUA_VERSION_NUM=`cat $1 | grep "#define LUA_VERSION_NUM" | awk '{ print $3 }'` + LUA_RELEASE=`cat $1 | grep "#define LUA_RELEASE[ ]" | cut -f2-` + + if [ $LUA_VERSION_NUM -ge 501 ]; then + return 1; + else + return 0; + fi +} + +if [ -z "$LUA_DIR" ]; then + for prefix in /usr /usr/local; do + if [ -e $prefix/include/lua.h ]; then + test_lua_version $prefix/include/lua.h + if [ $? -eq 1 ]; then + echo "using Lua $LUA_VERSION ($LUA_RELEASE) found at $prefix/include" + CFLAGS="$CFLAGS -I'$prefix/include'" + LDFLAGS="$LDFLAGS -L'$prefix/lib' -llua" + LUA=$prefix/lua + LUAC=$prefix/luac + break + fi + else + for dir in `ls -d $prefix/include/lua* 2> /dev/null`; do + if [ -e $dir/lua.h ]; then + test_lua_version $dir/lua.h + if [ $? -eq 1 ]; then + echo "using Lua $LUA_VERSION ($LUA_RELEASE) found at $dir" + CFLAGS="$CFLAGS -I$dir" + if [ -x "$prefix/bin/lua$LUA_VERSION" ]; then + LDFLAGS="$LDFLAGS -L'$prefix/lib' -llua$LUA_VERSION" + LUA=$prefix/bin/lua$LUA_VERSION + LUAC=$prefix/bin/luac$LUA_VERSION + elif [ -x "$prefix/bin/lua-$LUA_VERSION" ]; then + LDFLAGS="$LDFLAGS -L'$prefix/lib' -llua-$LUA_VERSION" + LUA=$prefix/bin/lua-$LUA_VERSION + LUAC=$prefix/bin/luac-$LUA_VERSION + else + echo "ERROR: found lua.h at $dir/lua.h but no matching lua and luac" + return 1 + fi + break + fi + fi + done + if [ -n "$LUAC" ]; then + break + fi + fi + done + + if [ -z "$LUAC" ]; then + echo "ERROR: no suitable lua found .. please install lua 5.1 or higher or use --with-lua" + return 1 + fi + +else + CFLAGS="$CFLAGS -I'$LUA_DIR/include'" + LDFLAGS="$LDFLAGS '$LUA_DIR/lib/liblua.a'" + LUA=$LUA_DIR/bin/lua + LUAC=$LUA_DIR/bin/luac +fi + +if [ -z "$BINDIR" ]; then + BINDIR=$PREFIX/bin +fi + +if [ -z "$ETCDIR" ]; then + ETCDIR=$PREFIX/etc +fi + +cat > include.mk <<EOF +# this file was created automatically +# do not edit this file directly +# use ./configure instead + +TARGET := '$TARGET' +CC := gcc +CFLAGS := $CFLAGS +LDFLAGS := $LDFLAGS +LUA := '$LUA' +LUAC := '$LUAC' +STRIP := strip +INSTALL := install + +PREFIX := '$PREFIX' +BINDIR := '$BINDIR' +ETCDIR := '$ETCDIR' +EOF + +exit 0 diff --git a/rhnop-server/l_pipe.c b/rhnop-server/l_pipe.c new file mode 100644 index 0000000..6b769a5 --- /dev/null +++ b/rhnop-server/l_pipe.c @@ -0,0 +1,137 @@ +/* + * rhnop + * + * Copyright (C) 2011 Christian Pointner <equinox@helsinki.at> + * + * This file is part of rhnop. + * + * rhnop 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 3 of the License, or + * any later version. + * + * rhnop 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 rhnop. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <lua.h> +#include <lauxlib.h> + +#include <string.h> +#include <errno.h> +#include <unistd.h> + +#include "l_pipe.h" + +static int pipefds_[2]; + +int pipe_init() +{ + return pipe(pipefds_); +} + +void pipe_close() +{ + close(pipefds_[0]); + close(pipefds_[1]); +} + +static int l_pipe_signal(lua_State *L) +{ + size_t len = 0, written = 0; + const char* data = luaL_checklstring(L, 1, &len); + len++; // also send trailing zero + + int ret = 0; + for(;;) { + ret = write(pipefds_[1], &(data[written]), len - written); + if(!ret) continue; + if(ret == -1 && (errno == EAGAIN || errno == EINTR)) continue; + + written += ret; + if(written == len) break; + + break; + } + + lua_pushinteger(L, ret); + return 1; +} + +static int l_pipe_getfd(lua_State *L) +{ + if(!lua_istable(L, -1)) + luaL_error(L, "can't retreive pipe fd"); + + lua_pushliteral(L, "fd"); + lua_gettable(L, -2); + return 1; +} + +static int l_pipe_dirty(lua_State *L) +{ + lua_pushboolean(L, 0); + return 1; +} + +static int l_pipe_getreadfd(lua_State *L) +{ + lua_newtable(L); + lua_pushliteral(L, "fd"); + lua_pushinteger(L, pipefds_[0]); + lua_settable(L, -3); + lua_pushliteral(L, "getfd"); + lua_pushcfunction(L, l_pipe_getfd); + lua_settable(L, -3); + lua_pushliteral(L, "dirty"); + lua_pushcfunction(L, l_pipe_dirty); + lua_settable(L, -3); + return 1; +} + +static int l_pipe_consume(lua_State *L) +{ + char data[17]; // 17 should be sufficient + size_t len = 0; + + int ret = 0; + for(;;) { + ret = read(pipefds_[0], &(data[len]), 1); + if(ret == 0) break; + if(ret == -1 && (errno == EAGAIN || errno == EINTR)) continue; + + if(data[len] == 0) + break; + + len += ret; + if(len >= sizeof(data)) { + ret = 0; + break; + } + } + + if(ret) + lua_pushstring(L, data); + else + lua_pushnil(L); + + return 1; +} + +static const struct luaL_reg pipe_funcs [] = { + { "signal", l_pipe_signal }, + { "getreadfd", l_pipe_getreadfd }, + { "consume", l_pipe_consume }, + { NULL, NULL } +}; + +LUALIB_API int luaopen_pipe(lua_State *L) +{ + luaL_register(L, LUA_PIPELIBNAME, pipe_funcs); + return 1; +} diff --git a/rhnop-server/l_pipe.h b/rhnop-server/l_pipe.h new file mode 100644 index 0000000..b67244b --- /dev/null +++ b/rhnop-server/l_pipe.h @@ -0,0 +1,33 @@ +/* + * rhnop + * + * Copyright (C) 2011 Christian Pointner <equinox@helsinki.at> + * + * This file is part of rhnop. + * + * rhnop 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 3 of the License, or + * any later version. + * + * rhnop 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 rhnop. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef NOPSYNCD_l_pipe_h_INCLUDED +#define NOPSYNCD_l_pipe_h_INCLUDED + +#include <lua.h> + +int pipe_init(); +void pipe_close(); + +#define LUA_PIPELIBNAME "pipe" +LUALIB_API int luaopen_pipe(lua_State *L); + +#endif diff --git a/rhnop-server/noprml b/rhnop-server/noprml new file mode 100755 index 0000000..31e8ab7 --- /dev/null +++ b/rhnop-server/noprml @@ -0,0 +1,59 @@ +#!/usr/bin/lua +-- +-- rhnop +-- +-- Copyright (C) 2011 Christian Pointner <equinox@helsinki.at> +-- +-- This file is part of rhnop. +-- +-- rhnop 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 3 of the License, or +-- any later version. +-- +-- rhnop 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 rhnop. If not, see <http://www.gnu.org/licenses/>. +-- +-- +-- send now and next cart# and length to nopsyncd +-- this script should be called by the now/next macro cart +-- the arguments should be: now# nowlen next# nextlen +-- + +local queue_name = "/rhnop" + +mq = require "mq" + +-- reading timestamp (milliseconds since epoch) +local p = assert(io.popen("/bin/date --utc '+%s %N'" , 'r')) +local time = assert(p:read('*l')) +p:close() +local s, ns = assert(string.match(time, "([0-9]+) ([0-9]+)")) +local timestamp = s*1000 + math.floor(ns/1000000) + +-- check arguments +if #arg < 4 then + io.stderr:write("too few parameters\n") + os.exit(1) +end + +-- open message queue +local q, err = mq.create(queue_name, "wo") +if q == nil then + io.stderr:write("creation of message queue failed: " .. err .. "\n") + os.exit(1) +end + +-- send out message to nopsyncd +local result, err = mq.send(q, timestamp .. " " .. arg[1] .. " " .. arg[2] .. " " .. arg[3] .. " " .. arg[4], 0) +if result == nil then + io.stderr:write("sending message failed: " .. err .. "\n") + os.exit(2) +end + +mq.close(q) diff --git a/rhnop-server/nopsyncd.c b/rhnop-server/nopsyncd.c new file mode 100644 index 0000000..63bef7f --- /dev/null +++ b/rhnop-server/nopsyncd.c @@ -0,0 +1,165 @@ +/* + * rhnop + * + * Copyright (C) 2011 Christian Pointner <equinox@helsinki.at> + * + * This file is part of rhnop. + * + * rhnop 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 3 of the License, or + * any later version. + * + * rhnop 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 rhnop. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <errno.h> +#include <string.h> +#include <pthread.h> + +#include <lua.h> +#include <lualib.h> +#include <lauxlib.h> + +#include "l_pipe.h" + +#define LUA_MAIN_LOOP_FUNC "main_loop" + +static const luaL_Reg nopsyncd_lualibs[] = { + {"", luaopen_base}, + {LUA_LOADLIBNAME, luaopen_package}, + {LUA_TABLIBNAME, luaopen_table}, + {LUA_STRLIBNAME, luaopen_string}, + {LUA_MATHLIBNAME, luaopen_math}, + {LUA_MATHLIBNAME, luaopen_io}, + {LUA_MATHLIBNAME, luaopen_os}, + {LUA_MATHLIBNAME, luaopen_pipe}, + {NULL, NULL} +}; + +int init_main_loop(lua_State *L, const char* filename) +{ + const luaL_Reg *lib = nopsyncd_lualibs; + for (; lib->func; lib++) { + lua_pushcfunction(L, lib->func); + lua_pushstring(L, lib->name); + lua_call(L, 1, 0); + } + + int ret = luaL_loadfile(L, filename); + if(ret) { + const char* err_str = luaL_checkstring(L, -1); + switch(ret) { + case LUA_ERRSYNTAX: fprintf(stderr, "luaL_loadfile() syntax error: %s\n", err_str); break; + case LUA_ERRMEM: fprintf(stderr, "luaL_loadfile() malloc error: %s\n", err_str); break; + case LUA_ERRFILE: fprintf(stderr, "lauL_loadfile() error: %s\n", err_str); break; + default: fprintf(stderr, "luaL_loadfile() unknown error: %s\n", err_str); break; + } + return -1; + } + + ret = lua_pcall(L, 0, 0, 0); + if(ret) { + const char* err_str = luaL_checkstring(L, -1); + switch(ret) { + case LUA_ERRRUN: fprintf(stderr, "lua_pcall() runtime error: %s\n", err_str); break; + case LUA_ERRMEM: fprintf(stderr, "lua_pcall() malloc error: %s\n", err_str); break; + case LUA_ERRERR: fprintf(stderr, "lua_pcall() error at error handler function: %s\n", err_str); break; + } + return -1; + } + + return 0; +} + +int call_main_loop(lua_State* L, const char* filename) +{ + lua_getglobal(L, LUA_MAIN_LOOP_FUNC); + if(!lua_isfunction(L, -1)) { + fprintf(stderr, "there is no function '%s' at file '%s'\n", LUA_MAIN_LOOP_FUNC, filename); + return -1; + }; + + int ret = lua_pcall(L, 0, 1, 0); + if(ret) { + const char* err_str = luaL_checkstring(L, -1); + switch(ret) { + case LUA_ERRRUN: fprintf(stderr, "lua_pcall(%s:%s) runtime error: %s\n", filename, LUA_MAIN_LOOP_FUNC, err_str); break; + case LUA_ERRMEM: fprintf(stderr, "lua_pcall(%s:%s) malloc error: %s\n", filename, LUA_MAIN_LOOP_FUNC, err_str); break; + case LUA_ERRERR: fprintf(stderr, "lua_pcall(%s:%s) error at error handler function: %s\n", filename, LUA_MAIN_LOOP_FUNC, err_str); break; + } + return -1; + } + + ret = lua_tointeger(L, 1); + return ret; +} + +void* main_loop(void* file) +{ + if(!file) + pthread_exit(NULL); + + + lua_State *L; + L = luaL_newstate(); + if(!L) { + fprintf(stderr, "error creating lua state\n"); + pthread_exit(NULL); + } + + int ret = init_main_loop(L, (char*)file); + if(!ret) + ret = call_main_loop(L, (char*)file); + + printf("%s returned with %d\n", (char*)file, ret); + + lua_close(L); + + /* this should bring down the other thread as well + at least this is true for the tcp-server thread */ + pipe_close(); + + pthread_exit(NULL); +} + +int main(int argc, char* argv[]) +{ + printf("starting nopsyncd...\n"); + + pthread_t qlistener, tcpserver; + + int ret = pipe_init(); + if(ret) { + fprintf(stderr, "Error creating pipe: %s\n", strerror(errno)); + return 1; + } + + ret = pthread_create(&qlistener, NULL, main_loop, "qlistener.lua"); + if(ret) { + fprintf(stderr, "Error creating qlistener thread (code: %d)\n", ret); + return 1; + } + pthread_detach(qlistener); /* can't kill this thread so don't join to it */ + + ret = pthread_create(&tcpserver, NULL, main_loop, "tcpserver.lua"); + if(ret) { + fprintf(stderr, "Error creating qlistener thread (code: %d)\n", ret); + return 1; + } + +/* this thread can't be cancelled so don't wait for it */ +/* pthread_join(qlistener, NULL); */ + pthread_join(tcpserver, NULL); + + printf("stopping nopsyncd.\n"); + return 0; +} diff --git a/rhnop-server/nopsyncd.conf b/rhnop-server/nopsyncd.conf new file mode 100644 index 0000000..0338cb6 --- /dev/null +++ b/rhnop-server/nopsyncd.conf @@ -0,0 +1,15 @@ +queue_name=/rhnop +tcp_host=* +tcp_port=2345 + +rddb_db=rivendell +rddb_host=127.0.0.1 +#rddb_port=3306 +rddb_user= +rddb_pwd= + +playlog_db=rhnop +playlog_host=127.0.0.1 +#playlog_port=3306 +playlog_user= +playlog_pwd= diff --git a/rhnop-server/playlog.lua b/rhnop-server/playlog.lua new file mode 100644 index 0000000..37b9ea6 --- /dev/null +++ b/rhnop-server/playlog.lua @@ -0,0 +1,90 @@ +-- +-- rhnop +-- +-- Copyright (C) 2011 Christian Pointner <equinox@helsinki.at> +-- +-- This file is part of rhnop. +-- +-- rhnop 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 3 of the License, or +-- any later version. +-- +-- rhnop 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 rhnop. If not, see <http://www.gnu.org/licenses/>. +-- + +require "luasql.mysql" + +-- CREATE DATABASE rhnop +-- GRANT select,insert,update ON rhnop.* TO 'nopsyncd' IDENTIFIED BY '123456'; +-- CREATE TABLE IF NOT EXISTS now (timestamp VARCHAR(16) PRIMARY KEY NOT NULL, cart INT NOT NULL, len INT, showtitle VARCHAR(255), title VARCHAR(255), artist VARCHAR(255), album VARCHAR(255), ismusic BOOLEAN) + +conf = require "conf" + +local playlog = {} + +function playlog:init() + local err + + self.env, err = luasql.mysql() + if self.env == nil then + return nil, err + end + + self.con, err = self.env:connect(conf.playlog_db, conf.playlog_user, conf.playlog_pwd, conf.playlog_host, conf.playlog_port) + if self.con == nil then + return nil, err + end + + local ret, err = self.con:setautocommit(true) + if ret == nil then + return nil, err + end + + return true +end + +function playlog:getLastCart() + local cur, err = self.con:execute("SELECT cart FROM now WHERE timestamp = (SELECT MAX(timestamp) FROM now)") + if cur == nil then + return nil, err + end + + local cart = cur:fetch() + if cart == nil then cart = 0 end + return cart +end + +function playlog:insertNowMusic(timestamp, cart, len, title, artist, album) + cart = tonumber(cart) + len = tonumber(len) + if cart < 400000 or cart > 450000 then + poolnum = 0 + else + poolnum = math.floor(cart/1000) - 399 + end + local cur, err = self.con:execute("INSERT into now VALUES(" .. self.con:escape(timestamp) .. ", " .. cart .. ", " .. len .. ", 'Musikpool " .. poolnum .. "', '" .. self.con:escape(title) .. "', '" .. self.con:escape(artist) .."', '" .. self.con:escape(album) .. "', 1)") + if cur == nil then + return nil, err + end + + return true +end + +function playlog:close() + if self.con then + self.con:close() + end + + if self.env then + self.env:close() + end +end + +return playlog diff --git a/rhnop-server/qlistener.lua b/rhnop-server/qlistener.lua new file mode 100755 index 0000000..8f2c0e2 --- /dev/null +++ b/rhnop-server/qlistener.lua @@ -0,0 +1,89 @@ +-- +-- rhnop +-- +-- Copyright (C) 2011 Christian Pointner <equinox@helsinki.at> +-- +-- This file is part of rhnop. +-- +-- rhnop 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 3 of the License, or +-- any later version. +-- +-- rhnop 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 rhnop. If not, see <http://www.gnu.org/licenses/>. +-- + +local last_cart = nil + +mq = require "mq" +playlog = require "playlog" +rddb = require "rddb" +conf = require "conf" + +function handle_now(timestamp, nowcart, nowlen) + local results, err = rddb:getCartInfo(nowcart); + if results == nil then + io.stderr:write("can't fetch cart info: " .. err .. "\n") + else +-- print(timestamp .. " Info: '" .. results.TITLE .. "' von '" .. results.ARTIST .. "' aus '" .. results.ALBUM .. "'") + local ret, err = playlog:insertNowMusic(timestamp, nowcart, nowlen, results.TITLE, results.ARTIST, results.ALBUM) + if ret == nil then + io.stderr:write("can't insert music info: " .. err .. "\n") + else + pipe.signal(timestamp) + end + end +end + +function handle_message(msg) + local timestamp, nowcart, nowlen, nextcart, nextlen = string.match(msg, "^(%d+) (%d+) (%d+) (%d+) (%d+)$"); + if not timestamp or not nowcart or not nowlen or not nextcart or not nextlen then + io.stderr:write("ignoring malformed message\n") + else + -- TODO better sanity checks: is now info new or just next? + if last_cart ~= nowcart then + last_cart = nowcart + handle_now(timestamp, nowcart, nowlen) + end + -- TODO handle next info + end +end + +function main_loop() + local q, err = mq.create(conf.queue_name, "ro") + if q == nil then + io.stderr:write("creation of message queue failed: " .. err .. "\n") + os.exit(1) + end + + local ret, err = playlog:init() + if ret == nil then + io.stderr:write("creation of playlog failed: " .. err .. "\n") + os.exit(1) + end + last_cart = assert(playlog:getLastCart()) + + local ret, err = rddb:init() + if ret == nil then + io.stderr:write("opening rivendell db failed: " .. err .. "\n") + playlog:close() + os.exit(1) + end + + while true do + local msg, prio = mq.receive(q) + if msg == nil then + io.stderr:write("recv error: " .. prio .. "\n") + rddb:close() + playlog:close() + os.exit(2) + end + handle_message(msg) + end +end
\ No newline at end of file diff --git a/rhnop-server/rddb.lua b/rhnop-server/rddb.lua new file mode 100644 index 0000000..c8037a9 --- /dev/null +++ b/rhnop-server/rddb.lua @@ -0,0 +1,75 @@ +-- +-- rhnop +-- +-- Copyright (C) 2011 Christian Pointner <equinox@helsinki.at> +-- +-- This file is part of rhnop. +-- +-- rhnop 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 3 of the License, or +-- any later version. +-- +-- rhnop 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 rhnop. If not, see <http://www.gnu.org/licenses/>. +-- + +require "luasql.mysql" + +conf = require "conf" + +local rddb = {} + +function rddb:init() + local err + + self.env, err = luasql.mysql() + if self.env == nil then + return nil, err + end + + self.con, err = self.env:connect(conf.rddb_db, conf.rddb_user, conf.rddb_pwd, conf.rddb_host, conf.rddb_port) + if self.con == nil then + return nil, err + end + + return true +end + +function rddb:getCartInfo(cartnum) + local cur, err = self.con:execute("select TITLE,ARTIST,ALBUM from CART where NUMBER = " .. self.con:escape(cartnum)); + if cur == nil then + return nil, err + end + + if cur:numrows() ~= 1 then + return nil, "nothing found in rivendell db" + end + + local results = {} + results, err = cur:fetch(results, "a") + cur:close() + + if results.TITLE == nil then results.TITLE = "" end + if results.ARTIST == nil then results.ARTIST = "" end + if results.ALBUM == nil then results.ALBUM = "" end + + return results, err +end + +function rddb:close() + if self.con then + self.con:close() + end + + if self.env then + self.env:close() + end +end + +return rddb diff --git a/rhnop-server/tcpserver.lua b/rhnop-server/tcpserver.lua new file mode 100755 index 0000000..bd945b6 --- /dev/null +++ b/rhnop-server/tcpserver.lua @@ -0,0 +1,128 @@ +-- +-- rhnop +-- +-- Copyright (C) 2011 Christian Pointner <equinox@helsinki.at> +-- +-- This file is part of rhnop. +-- +-- rhnop 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 3 of the License, or +-- any later version. +-- +-- rhnop 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 rhnop. If not, see <http://www.gnu.org/licenses/>. +-- + +require "socket" + +conf = require "conf" + +function init_server() + local server = assert(socket.tcp()) + + assert(server:setoption('reuseaddr', true)) + assert(server:bind(conf.tcp_host, conf.tcp_port)) + assert(server:listen(5)) + + return server +end + +local clients = {} + +function add_client(hdl) + -- print("new client(" .. hdl:getfd() .. ") from " .. hdl:getpeername()) + local client = {} + client.buffer = "" + client.hdl = hdl + client.getfd = function() return hdl:getfd() end + client.dirty = function() return hdl:dirty() end + table.insert(clients, client) +end + +function remove_client(c) + local idx = 0 + for idx, client in ipairs(clients) do + if client == c then + break + end + end + + -- print("removing client(" .. c.hdl:getfd() .. ")") + c.hdl:close() + table.remove(clients, idx) +end + +function cleanup_clients() + for _, client in ipairs(clients) do + client:close() + end +end + +function clients_get_writeables() + local fds = {} + + for _, client in ipairs(clients) do + if client.buffer ~= "" then + table.insert(fds, client) + end + end + + return fds +end + +function clients_senddata(timestamp) + for _, client in ipairs(clients) do + client.buffer = client.buffer .. timestamp .. "\n" + end +end + +function main_loop() + local pipefd = pipe.getreadfd() + + server = init_server() + while true do + local readables, writeables, err = socket.select({ pipefd , server , unpack(clients) } , clients_get_writeables()) + if err then + print("select returned with error: " .. err) + return -1 + else + for _, input in ipairs(readables) do + if input == pipefd then + local timestamp = pipe.consume() + if timestamp == nil then + return 2 + end + -- print("pipe was signaled with timestamp: " .. timestamp) + clients_senddata(timestamp) + elseif input == server then + local client = assert(server:accept()) + add_client(client) + else + if input.hdl then + -- receive is insanely stupid, therefore we must always read one byte only + local _, err = input.hdl:receive(1) + if err then + remove_client(input) + end + end + end + end + + for _, output in ipairs(writeables) do + local ret = assert(output.hdl:send(output.buffer)) + output.buffer = string.sub(output.buffer, ret+1) + end + end + end + + server:close() + cleanup_clients() + + return 0 +end |