diff options
-rw-r--r-- | Makefile.am | 3 | ||||
-rwxr-xr-x | autogen.sh | 89 | ||||
-rw-r--r-- | configure.in | 65 | ||||
-rw-r--r-- | src/Makefile.am | 23 | ||||
-rw-r--r-- | src/muniad.cc | 495 |
5 files changed, 675 insertions, 0 deletions
diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 0000000..06d6b0e --- /dev/null +++ b/Makefile.am @@ -0,0 +1,3 @@ +AUTOMAKE_OPTIONS = gnu +SUBDIRS = src tools +DISTDIRS = src tools diff --git a/autogen.sh b/autogen.sh new file mode 100755 index 0000000..6e23e5b --- /dev/null +++ b/autogen.sh @@ -0,0 +1,89 @@ +#!/bin/sh +# Run this to generate all the initial makefiles, etc. +# This was lifted from the Gimp, and adapted slightly by +# Raph Levien, slightly hacked for xine by Daniel Caujolle-Bert. + +DIE=0 + +PROG=anoid + +# Check how echo works in this /bin/sh +case `echo -n` in +-n) _echo_n= _echo_c='\c';; +*) _echo_n=-n _echo_c=;; +esac + + +(autoconf --version) < /dev/null > /dev/null 2>&1 || { + echo + echo "You must have autoconf installed to compile $PROG." + echo "Download the appropriate package for your distribution," + echo "or get the source tarball at ftp://ftp.gnu.org/pub/gnu/" + DIE=1 +} + +(libtool --version) < /dev/null > /dev/null 2>&1 || { + echo + echo "You must have libtool installed to compile $PROG." + echo "Get ftp://ftp.gnu.org/pub/gnu/libtool-1.4.tar.gz" + echo "(or a newer version if it is available)" + DIE=1 +} + +(automake --version) < /dev/null > /dev/null 2>&1 || { + echo + echo "You must have automake installed to compile $PROG." + echo "Get ftp://ftp.gnu.org/pub/gnu/automake-1.3.tar.gz" + echo "(or a newer version if it is available)" + DIE=1 +} + +(aclocal --version) < /dev/null > /dev/null 2>&1 || { + echo + echo "**Error**: Missing aclocal. The version of automake" + echo "installed doesn't appear recent enough." + echo "Get ftp://ftp.gnu.org/pub/gnu/automake-1.3.tar.gz" + echo "(or a newer version if it is available)" + DIE=1 +} + +if [ "$DIE" -eq 1 ]; then + exit 1 +fi + +# Create testfiles +TESTDIRS="src" +for d in $TESTDIRS +do + if [ ! -f $d/Makefile.am.test ] + then + touch $d/Makefile.am.test + fi +done + +aclocalinclude="$ACLOCAL_FLAGS"; \ +(echo $_echo_n " + Running aclocal: $_echo_c"; \ + aclocal $aclocalinclude; \ + echo "done.") && \ +(echo $_echo_n " + Running libtoolize: $_echo_c"; \ + libtoolize --force --copy >/dev/null 2>&1; \ + echo "done.") && \ +(echo $_echo_n " + Running autoheader: $_echo_c"; \ + autoheader; \ + echo "done.") && \ +(echo $_echo_n " + Running automake: $_echo_c"; \ + automake --gnu --add-missing --copy; \ + echo "done.") && \ +(echo $_echo_n " + Running autoconf: $_echo_c"; \ + autoconf; \ + echo "done.") + +rm -f config.cache + +for d in $TESTDIRS +do + if [ -f $d/Makefile.am.test ] + then + rm $d/Makefile.am.test + fi +done diff --git a/configure.in b/configure.in new file mode 100644 index 0000000..4a9e870 --- /dev/null +++ b/configure.in @@ -0,0 +1,65 @@ +# Filename: configure.in + +AC_INIT(src/muniad.cc) + +VERSION="0.0.1" +AM_INIT_AUTOMAKE( munia, $VERSION ) + +dnl ====================== +dnl Compile with debug options +dnl ====================== +AC_ARG_WITH(debug, + [ --with-debug build with debug support (default=no)], + [], + [with_debug=no]) +if test x$with_debug == xyes; then + AC_MSG_WARN([*** Building with debug support!]) + AC_DEFINE_UNQUOTED(WITH_DEBUG, , [The project is configured to use debug output]) + CXXFLAGS="$CXXFLAGS -D_FORTIFY_SOURCE=2 -fstack-protector -Wall -Werror -g -O0" +fi + +dnl ====================== +dnl Init pkg-config +dnl ====================== +PKG_PROG_PKG_CONFIG(0.23) + +AC_PROG_CXX + +AM_CONFIG_HEADER(config.h) +AC_STDC_HEADERS + +dnl ====================== +dnl Check for getopt +dnl ====================== +AC_HAVE_HEADERS(getopt.h) + +dnl ====================== +dnl Check for libwebsockets library +dnl ====================== +tmp_CXXFLAGS="$CXXFLAGS" +tmp_CPPFLAGS="$CPPFLAGS" +tmp_CFLAGS="$CFLAGS" +tmp_LDFLAGS="$LDFLAGS" +tmp_LIBS="$LIBS" +CXXFLAGS="" +CPPFLAGS="" +CFLAGS="" +LDFLAGS="" +LIBS="" +AC_CHECK_HEADER(libwebsockets.h, , AC_MSG_ERROR([*** libwebsockets header file not found!])) +AC_CHECK_LIB(websockets, libwebsocket_create_context, , AC_MSG_ERROR([*** libwebsockets library not found!])) +LIBWEBSOCKETS_CFLAGS="$CXXFLAGS $CPPFLAGS $CFLAGS" +LIBWEBSOCKETS_LIBS="$LDFLAGS $LIBS" +CXXFLAGS="$tmp_CXXFLAGS" +CPPFLAGS="$tmp_CPPFLAGS" +CFLAGS="$tmp_CFLAGS" +LDFLAGS="$tmp_LDFLAGS" +LIBS="$tmp_LIBS" +AC_SUBST(LIBWEBSOCKETS_CFLAGS) +AC_SUBST(LIBWEBSOCKETS_LIBS) + +AC_OUTPUT( + Makefile + src/Makefile + tools/Makefile) + diff --git a/src/Makefile.am b/src/Makefile.am new file mode 100644 index 0000000..ec4ada4 --- /dev/null +++ b/src/Makefile.am @@ -0,0 +1,23 @@ +SUBDIRS = + +bin_PROGRAMS = muniad + +muniad_LDADD = $(LIBWEBSOCKETS_LIBS) + +muniad_CXXFLAGS = $(LIBWEBSOCKETS_CFLAGS) + +muniad_SOURCES = \ + muniad.cc + +EXTRA_DIST = + +################ +# Test Section # +################ + +TEST_SOURCE_DEPS = ${muniad_SOURCES} ${EXTRA_DIST} +TEST_SCRIPT_DIR = $(top_srcdir)/tools + +include ${TEST_SCRIPT_DIR}/Makefile.am.test + +include Makefile.am.test
\ No newline at end of file diff --git a/src/muniad.cc b/src/muniad.cc new file mode 100644 index 0000000..f5d4d6a --- /dev/null +++ b/src/muniad.cc @@ -0,0 +1,495 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set et sw=2 ts=2: */ +/*************************************************************************** + * muniad.cc + * + * Thu Feb 16 15:03:28 CET 2012 + * Copyright 2012 Bent Bisballe Nyeng + * deva@aasimon.org + ****************************************************************************/ + +/* + * This file is part of Munia. + * + * Munia 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. + * + * Munia 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 Munia; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <getopt.h> +#include <string.h> +#include <sys/time.h> + +#include <libwebsockets.h> + +#define LWS_NO_FORK + +static int close_testing; + +/* + * This demo server shows how to use libwebsockets for one or more + * websocket protocols in the same server + * + * It defines the following websocket protocols: + * lws-mirror-protocol: copies any received packet to every connection also + * using this protocol, including the sender + */ + +enum demo_protocols { + /* always first */ + PROTOCOL_HTTP = 0, + PROTOCOL_LWS_MIRROR, + /* always last */ + DEMO_PROTOCOL_COUNT +}; + +#define LOCAL_RESOURCE_PATH "." + +/* this protocol server (always the first one) just knows how to do HTTP */ + +static int callback_http(struct libwebsocket_context * context, + struct libwebsocket *wsi, + enum libwebsocket_callback_reasons reason, void *user, + void *in, size_t len) +{ + char client_name[128]; + char client_ip[128]; + + switch (reason) { + case LWS_CALLBACK_HTTP: + fprintf(stderr, "serving HTTP URI %s\n", (char *)in); + + if(in && strcmp((const char *)in, "/favicon.ico") == 0) { + if(libwebsockets_serve_http_file(wsi, LOCAL_RESOURCE_PATH"/favicon.ico", "image/x-icon")) + fprintf(stderr, "Failed to send favicon\n"); + break; + } + + /* send the script... when it runs it'll start websockets */ + if (libwebsockets_serve_http_file(wsi, LOCAL_RESOURCE_PATH"/test.html", "text/html")) + fprintf(stderr, "Failed to send HTTP file\n"); + break; + + /* + * callback for confirming to continue with client IP appear in + * protocol 0 callback since no websocket protocol has been agreed + * yet. You can just ignore this if you won't filter on client IP + * since the default uhandled callback return is 0 meaning let the + * connection continue. + */ + + case LWS_CALLBACK_FILTER_NETWORK_CONNECTION: + libwebsockets_get_peer_addresses((int)(long)user, client_name, sizeof(client_name), client_ip, sizeof(client_ip)); + fprintf(stderr, "Received network connect from %s (%s)\n", client_name, client_ip); + /* if we returned non-zero from here, we kill the connection */ + break; + + default: + break; + } + + return 0; +} + + +/* + * this is just an example of parsing handshake headers, you don't need this + * in your code unless you will filter allowing connections by the header + * content + */ + +static void dump_handshake_info(struct lws_tokens *lwst) +{ + int n; + static const char *token_names[WSI_TOKEN_COUNT] = { + /*[WSI_TOKEN_GET_URI] =*/ "GET URI", + /*[WSI_TOKEN_HOST] =*/ "Host", + /*[WSI_TOKEN_CONNECTION] =*/ "Connection", + /*[WSI_TOKEN_KEY1] =*/ "key 1", + /*[WSI_TOKEN_KEY2] =*/ "key 2", + /*[WSI_TOKEN_PROTOCOL] =*/ "Protocol", + /*[WSI_TOKEN_UPGRADE] =*/ "Upgrade", + /*[WSI_TOKEN_ORIGIN] =*/ "Origin", + /*[WSI_TOKEN_DRAFT] =*/ "Draft", + /*[WSI_TOKEN_CHALLENGE] =*/ "Challenge", + + /* new for 04 */ + /*[WSI_TOKEN_KEY] =*/ "Key", + /*[WSI_TOKEN_VERSION] =*/ "Version", + /*[WSI_TOKEN_SWORIGIN] =*/ "Sworigin", + + /* new for 05 */ + /*[WSI_TOKEN_EXTENSIONS] =*/ "Extensions", + + /* client receives these */ + /*[WSI_TOKEN_ACCEPT] =*/ "Accept", + /*[WSI_TOKEN_NONCE] =*/ "Nonce", + /*[WSI_TOKEN_HTTP] =*/ "Http", + /*[WSI_TOKEN_MUXURL] =*/ "MuxURL", + }; + + for (n = 0; n < WSI_TOKEN_COUNT; n++) { + if (lwst[n].token == NULL) continue; + fprintf(stderr, " %s = %s\n", token_names[n], lwst[n].token); + } +} + + +/* lws-mirror_protocol */ + +#define MAX_MESSAGE_QUEUE 1024 + +struct per_session_data__lws_mirror { + struct libwebsocket *wsi; + int ringbuffer_tail; +}; + +struct a_message { + void *payload; + size_t len; +}; + +#include <string> +#include <map> + +typedef int boxid_t; +class Box { +public: + Box(std::string data) {} + Box() {} + + boxid_t id; + std::string title; + std::string description; + int x, y; +}; + +std::map<boxid_t, Box> boxes; + +/* +Protocol: + +Server -> client: + update [id] [title] [description]; + move [id] [x] [y]; + add [title] [description]; + +Client -> server: + update [id] [title] [description]; + move [id] [x] [y]; + add [id] [title] [description] [x] [y]; + +title and description are " encapsulated utf-8 string with " escaped with a backslash. +x and y are integers as strings +id are an integer as a string + */ + +static struct a_message ringbuffer[MAX_MESSAGE_QUEUE]; +static int ringbuffer_head; + +static int callback_lws_mirror(struct libwebsocket_context * context, + struct libwebsocket *wsi, + enum libwebsocket_callback_reasons reason, + void *user, void *in, size_t len) +{ + int n; + struct per_session_data__lws_mirror *pss = (struct per_session_data__lws_mirror *)user; + + switch (reason) { + + case LWS_CALLBACK_ESTABLISHED: + { + fprintf(stderr, "callback_lws_mirror: LWS_CALLBACK_ESTABLISHED\n"); + pss->ringbuffer_tail = ringbuffer_head; + pss->wsi = wsi; + } + break; + + case LWS_CALLBACK_SERVER_WRITEABLE: + { + printf("LWS_CALLBACK_SERVER_WRITEABLE\n"); + if(close_testing) break; + + if(pss->ringbuffer_tail != ringbuffer_head) { + n = libwebsocket_write(wsi, (unsigned char *) + ringbuffer[pss->ringbuffer_tail].payload + + LWS_SEND_BUFFER_PRE_PADDING, + ringbuffer[pss->ringbuffer_tail].len, + LWS_WRITE_TEXT); + if(n < 0) { + fprintf(stderr, "ERROR writing to socket"); + exit(1); + } + + if(pss->ringbuffer_tail == (MAX_MESSAGE_QUEUE - 1)) pss->ringbuffer_tail = 0; + else pss->ringbuffer_tail++; + + if(((ringbuffer_head - pss->ringbuffer_tail) % MAX_MESSAGE_QUEUE) < (MAX_MESSAGE_QUEUE - 15)) + libwebsocket_rx_flow_control(wsi, 1); + + libwebsocket_callback_on_writable(context, wsi); + } + } + break; + + case LWS_CALLBACK_BROADCAST: + printf("LWS_CALLBACK_BROADCAST\n"); + n = libwebsocket_write(wsi, (unsigned char*)in, len, LWS_WRITE_TEXT); + if (n < 0) fprintf(stderr, "mirror write failed\n"); + break; + + case LWS_CALLBACK_RECEIVE: + { + printf("LWS_CALLBACK_RECEIVE\n"); + + printf("%s\n", (char*)in); + std::string data; + data.append((char*)in, len); + + std::string cmd = data.substr(0, data.find(' ')); + printf("Cmd: %s\n", cmd.c_str()); + + if(cmd == "add") { + printf("Add\n"); + } else if(cmd == "move") { + printf("Move\n"); + } else if(cmd == "update") { + printf("Update\n"); + } else { // unknown command + + } + + struct a_message &msg = ringbuffer[ringbuffer_head]; + if(msg.payload) { + free(msg.payload); + msg.payload = NULL; + } + + msg.payload = malloc(LWS_SEND_BUFFER_PRE_PADDING + len + LWS_SEND_BUFFER_POST_PADDING); + msg.len = len; + memcpy((char *)msg.payload + LWS_SEND_BUFFER_PRE_PADDING, in, len); + if(ringbuffer_head == (MAX_MESSAGE_QUEUE - 1)) ringbuffer_head = 0; + else ringbuffer_head++; + + if(((ringbuffer_head - pss->ringbuffer_tail) % MAX_MESSAGE_QUEUE) > (MAX_MESSAGE_QUEUE - 10)) + libwebsocket_rx_flow_control(wsi, 0); + + libwebsocket_callback_on_writable_all_protocol(libwebsockets_get_protocol(wsi)); + } + break; + /* + * this just demonstrates how to use the protocol filter. If you won't + * study and reject connections based on header content, you don't need + * to handle this callback + */ + + case LWS_CALLBACK_FILTER_PROTOCOL_CONNECTION: + printf("LWS_CALLBACK_FILTER_PROTOCOL_CONNECTION\n"); + dump_handshake_info((struct lws_tokens *)(long)user); + /* you could return non-zero here and kill the connection */ + break; + + default: + break; + } + + return 0; +} + + +/* list of supported protocols and callbacks */ + +static struct libwebsocket_protocols protocols[] = { + /* first protocol must always be HTTP handler */ + + { + "http-only", /* name */ + callback_http, /* callback */ + 0 /* per_session_data_size */ + }, + { + "lws-mirror-protocol", + callback_lws_mirror, + sizeof(struct per_session_data__lws_mirror) + }, + { + NULL, NULL, 0 /* End of list */ + } +}; + +static struct option options[] = { + { "help", no_argument, NULL, 'h' }, + { "port", required_argument, NULL, 'p' }, + { "ssl", no_argument, NULL, 's' }, + { "killmask", no_argument, NULL, 'k' }, + { "interface", required_argument, NULL, 'i' }, + { "closetest", no_argument, NULL, 'c' }, + { NULL, 0, 0, 0 } +}; + +int main(int argc, char **argv) +{ + int n = 0; + const char *cert_path = + LOCAL_RESOURCE_PATH"/libwebsockets-test-server.pem"; + const char *key_path = + LOCAL_RESOURCE_PATH"/libwebsockets-test-server.key.pem"; + unsigned char buf[LWS_SEND_BUFFER_PRE_PADDING + 1024 + + LWS_SEND_BUFFER_POST_PADDING]; + int port = 7681; + int use_ssl = 0; + struct libwebsocket_context *context; + int opts = 0; + char interface_name[128] = ""; + const char * interface = NULL; +#ifdef LWS_NO_FORK + unsigned int oldus = 0; +#endif + + fprintf(stderr, "libwebsockets test server\n" + "(C) Copyright 2010-2011 Andy Green <andy@warmcat.com> " + "licensed under LGPL2.1\n"); + + while (n >= 0) { + n = getopt_long(argc, argv, "ci:khsp:", options, NULL); + if (n < 0) + continue; + switch (n) { + case 's': + use_ssl = 1; + break; + case 'k': + opts = LWS_SERVER_OPTION_DEFEAT_CLIENT_MASK; + break; + case 'p': + port = atoi(optarg); + break; + case 'i': + strncpy(interface_name, optarg, sizeof interface_name); + interface_name[(sizeof interface_name) - 1] = '\0'; + interface = interface_name; + break; + case 'c': + close_testing = 1; + fprintf(stderr, " Close testing mode -- closes on " + "client after 50 dumb increments" + "and suppresses lws_mirror spam\n"); + break; + case 'h': + fprintf(stderr, "Usage: test-server " + "[--port=<p>] [--ssl]\n"); + exit(1); + } + } + + if (!use_ssl) + cert_path = key_path = NULL; + + context = libwebsocket_create_context(port, interface, protocols, + libwebsocket_internal_extensions, + cert_path, key_path, -1, -1, opts); + if (context == NULL) { + fprintf(stderr, "libwebsocket init failed\n"); + return -1; + } + + buf[LWS_SEND_BUFFER_PRE_PADDING] = 'x'; + +#ifdef LWS_NO_FORK + + /* + * This example shows how to work with no forked service loop + */ + + fprintf(stderr, " Using no-fork service loop\n"); + + while (1) { + /* + * This example server does not fork or create a thread for + * websocket service, it all runs in this single loop. So, + * we have to give the websockets an opportunity to service + * "manually". + * + * If no socket is needing service, the call below returns + * immediately and quickly. + */ + + libwebsocket_service(context, 50); + } + +#else + + /* + * This example shows how to work with the forked websocket service loop + */ + + fprintf(stderr, " Using forked service loop\n"); + + /* + * This forks the websocket service action into a subprocess so we + * don't have to take care about it. + */ + + n = libwebsockets_fork_service_loop(context); + if (n < 0) { + fprintf(stderr, "Unable to fork service loop %d\n", n); + return 1; + } + + while (1) { + + usleep(50000); + + /* + * This broadcasts to all dumb-increment-protocol connections + * at 20Hz. + * + * We're just sending a character 'x', in these examples the + * callbacks send their own per-connection content. + * + * You have to send something with nonzero length to get the + * callback actions delivered. + * + * We take care of pre-and-post padding allocation. + */ + + libwebsockets_broadcast(&protocols[PROTOCOL_DUMB_INCREMENT], + &buf[LWS_SEND_BUFFER_PRE_PADDING], 1); + } + +#endif + + libwebsocket_context_destroy(context); + + return 0; +} + +#ifdef TEST_MUNIAD +//Additional dependency files +//deps: +//Required cflags (autoconf vars may be used) +//cflags: +//Required link options (autoconf vars may be used) +//libs: +#include "test.h" + +TEST_BEGIN; + +// TODO: Put some testcode here (see test.h for usable macros). +TEST_TRUE(false, "No tests yet!"); + +TEST_END; + +#endif/*TEST_MUNIAD*/ |