update
authorMichael J. Pounders <snowblind@bellsouth.net>
Mon, 22 Sep 2014 01:50:44 +0000 (21:50 -0400)
committerMichael J. Pounders <snowblind@bellsouth.net>
Mon, 22 Sep 2014 01:50:44 +0000 (21:50 -0400)
shm_utils/mon-umurmurd/README.md
shm_utils/umurmurd-websocket/README.md

shm_utils/mon-umurmurd/README.md [new file with mode: 0644]
shm_utils/umurmurd-websocket/README.md [new file with mode: 0644]
shm_utils/umurmurd-websocket/src/uMurmurd_Websocket.c [new file with mode: 0644]

diff --git a/shm_utils/mon-umurmurd/README.md b/shm_utils/mon-umurmurd/README.md
new file mode 100644 (file)
index 0000000..9d0d519
--- /dev/null
@@ -0,0 +1,7 @@
+mon-umurmurd\r
+============\r
+\r
+outputs server/client data to term useing the sharedmemory API added to umurmur for testing\r
+\r
+make\r
+./mon-umurmurd
\ No newline at end of file
diff --git a/shm_utils/umurmurd-websocket/README.md b/shm_utils/umurmurd-websocket/README.md
new file mode 100644 (file)
index 0000000..fa2611c
--- /dev/null
@@ -0,0 +1,29 @@
+uMurmurd-Websocket - HTTP/JSON server example\r
+=============================================\r
+\r
+HTTP/JSON server in one. Useing a js prettyprinter type lib to display the output\r
+It not the best looking but its just a demo. Hopeing someone else with better web \r
+skills can make something better.\r
+  \r
+\r
+Instructions for building from source\r
+-------------------------------------\r
+1. Requirements\r
+  * [OpenSSL](http://www.openssl.org/)\r
+       * [libwebsockets](http://libwebsockets.org) library. I used this [tarball]([http://git.libwebsockets.org/cgi-bin/cgit/libwebsockets/snapshot/libwebsockets-1.23-chrome32-firefox24.tar.gz) \r
+  * [Jansson](www.digip.org/jansson/) library.  I used this [tarball](http://www.digip.org/jansson/releases/jansson-2.6.tar.gz) Jansson is a C library for encoding, decoding and manipulating JSON data\r
+  * [CMake](http://cmake.org) \r
+\r
+2. Build - CMake\r
+       * Create a build folder and cd into it\r
+       * `cmake ../`\r
+       * `make`\r
+\r
+3. Run\r
+  * start umurmurd from this git like this ./umurmurd -d -c <conf file>\r
+       - the -d option is just to make it easier to see debug info\r
+  * start uMurmurd-Websocket with ./uMurmurd-Websocket --ssl \r
+       - option --ssl is for https://, http:// otherwise\r
+  * point web browser to https://localhost:7681      \r
+\r
+          
\ No newline at end of file
diff --git a/shm_utils/umurmurd-websocket/src/uMurmurd_Websocket.c b/shm_utils/umurmurd-websocket/src/uMurmurd_Websocket.c
new file mode 100644 (file)
index 0000000..b5e7e69
--- /dev/null
@@ -0,0 +1,632 @@
+/*
+ * uMurmurd Websocket server - HTTP/JSON serverexample
+ *
+ * Copyright (C) 2014 Michael P. Pounders <>
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation:
+ *  version 2.1 of the License.
+ *
+ *  This library 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
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ *  MA  02110-1301  USA
+ */
+#ifdef CMAKE_BUILD
+#include "lws_config.h"
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <string.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <assert.h>
+
+#include <syslog.h>
+
+#include <signal.h>
+
+#include <jansson.h>
+
+#include <libwebsockets.h>
+#include "../../../src/sharedmemory.h"
+
+int max_poll_elements;
+
+struct pollfd *pollfds;
+int *fd_lookup;
+int count_pollfds;
+int force_exit = 0;
+
+enum demo_protocols {
+       /* always first */
+       PROTOCOL_HTTP = 0,
+
+       PROTOCOL_JSON_UMURMURD,
+
+       /* always last */
+       DEMO_PROTOCOL_COUNT
+};
+
+
+
+char *resource_path = "../web";
+
+/*
+ * We take a strict whitelist approach to stop ../ attacks
+ */
+
+struct serveable {
+       const char *urlpath;
+       const char *mimetype;
+}; 
+
+static const struct serveable whitelist[] = {
+       { "/favicon.ico", "image/x-icon" },
+  { "/css/mon_umurmurd.css", "text/css" },
+  { "/css/json.human.css", "text/css" },
+  { "/js/crel.js", "text/javascript" },
+  { "/js/json.human.js", "text/javascript" },
+  { "/js/jquery.min.js", "text/javascript" },
+  
+       /* last one is the default served if no match */
+       { "/mon_umurmurd.html", "text/html" },
+};
+
+struct per_session_data__http {
+       int fd;
+};
+
+/* 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)
+{
+#if 0
+       char client_name[128];
+       char client_ip[128];
+#endif
+       char buf[256];
+       char leaf_path[1024];
+       int n, m;
+       unsigned char *p;
+       static unsigned char buffer[4096];
+       struct stat stat_buf;
+       struct per_session_data__http *pss =
+                       (struct per_session_data__http *)user;
+
+       switch (reason) {
+       case LWS_CALLBACK_HTTP:
+
+               /* check for the "send a big file by hand" example case */
+
+               if (!strcmp((const char *)in, "/leaf.jpg")) {
+                       if (strlen(resource_path) > sizeof(leaf_path) - 10)
+                               return -1;
+                       sprintf(leaf_path, "%s/leaf.jpg", resource_path);
+
+                       /* well, let's demonstrate how to send the hard way */
+
+                       p = buffer;
+
+                       pss->fd = open(leaf_path, O_RDONLY);
+
+                       if (pss->fd < 0)
+                               return -1;
+
+                       fstat(pss->fd, &stat_buf);
+
+                       /*
+                        * we will send a big jpeg file, but it could be
+                        * anything.  Set the Content-Type: appropriately
+                        * so the browser knows what to do with it.
+                        */
+
+                       p += sprintf((char *)p,
+                               "HTTP/1.0 200 OK\x0d\x0a"
+                               "Server: libwebsockets\x0d\x0a"
+                               "Content-Type: image/jpeg\x0d\x0a"
+                                       "Content-Length: %u\x0d\x0a\x0d\x0a",
+                                       (unsigned int)stat_buf.st_size);
+
+                       /*
+                        * send the http headers...
+                        * this won't block since it's the first payload sent
+                        * on the connection since it was established
+                        * (too small for partial)
+                        */
+
+                       n = libwebsocket_write(wsi, buffer,
+                                  p - buffer, LWS_WRITE_HTTP);
+
+                       if (n < 0) {
+                               close(pss->fd);
+                               return -1;
+                       }
+                       /*
+                        * book us a LWS_CALLBACK_HTTP_WRITEABLE callback
+                        */
+                       libwebsocket_callback_on_writable(context, wsi);
+                       break;
+               }
+
+               /* if not, send a file the easy way */
+
+               for (n = 0; n < (sizeof(whitelist) / sizeof(whitelist[0]) - 1); n++)
+                       if (in && strcmp((const char *)in, whitelist[n].urlpath) == 0)
+                               break;
+
+               sprintf(buf, "%s%s", resource_path, whitelist[n].urlpath);
+
+               if (libwebsockets_serve_http_file(context, wsi, buf, whitelist[n].mimetype))
+                       return -1; /* through completion or error, close the socket */
+
+               /*
+                * notice that the sending of the file completes asynchronously,
+                * we'll get a LWS_CALLBACK_HTTP_FILE_COMPLETION callback when
+                * it's done
+                */
+
+               break;
+
+       case LWS_CALLBACK_HTTP_FILE_COMPLETION:
+//             lwsl_info("LWS_CALLBACK_HTTP_FILE_COMPLETION seen\n");
+               /* kill the connection after we sent one file */
+               return -1;
+
+       case LWS_CALLBACK_HTTP_WRITEABLE:
+               /*
+                * we can send more of whatever it is we were sending
+                */
+
+               do {
+                       n = read(pss->fd, buffer, sizeof buffer);
+                       /* problem reading, close conn */
+                       if (n < 0)
+                               goto bail;
+                       /* sent it all, close conn */
+                       if (n == 0)
+                               goto bail;
+                       /*
+                        * because it's HTTP and not websocket, don't need to take
+                        * care about pre and postamble
+                        */
+                       m = libwebsocket_write(wsi, buffer, n, LWS_WRITE_HTTP);
+                       if (m < 0)
+                               /* write failed, close conn */
+                               goto bail;
+                       if (m != n)
+                               /* partial write, adjust */
+                               lseek(pss->fd, m - n, SEEK_CUR);
+
+               } while (!lws_send_pipe_choked(wsi));
+               libwebsocket_callback_on_writable(context, wsi);
+               break;
+
+bail:
+               close(pss->fd);
+               return -1;
+
+       /*
+        * 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:
+#if 0
+               libwebsockets_get_peer_addresses(context, wsi, (int)(long)in, client_name,
+                            sizeof(client_name), client_ip, sizeof(client_ip));
+
+               fprintf(stderr, "Received network connect from %s (%s)\n",
+                                                       client_name, client_ip);
+#endif
+               /* 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 libwebsocket *wsi)
+{
+       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",
+       };
+       char buf[256];
+
+       for (n = 0; n < WSI_TOKEN_COUNT; n++) {
+               if (!lws_hdr_total_length(wsi, n))
+                       continue;
+
+               lws_hdr_copy(wsi, buf, sizeof buf, n);
+
+               fprintf(stderr, "    %s = %s\n", token_names[n], buf);
+       }
+}
+
+void *getJsonData( unsigned char * buf, int *n )
+{
+
+int cc;
+json_t *jarr1;
+char *result = NULL;
+
+json_t *root = NULL, *server = NULL, *client, *clients;
+
+    root = json_object();
+    clients = json_object();
+
+          
+          server = json_pack( "{s:{:s:i,s:i}}", 
+                              "server", 
+                              "clients_max", shmptr->server_max_clients, 
+                              "clients_connected", shmptr->clientcount );
+    
+        json_object_update( root, server );
+          
+        if( shmptr->clientcount )
+        {  
+          jarr1 = json_array();
+              
+          for( cc = 0 ; cc < shmptr->server_max_clients ; cc++ )
+          {
+          
+          if( !shmptr->client[cc].authenticated )
+            continue;
+                                                                               
+          client = json_pack( "{:s:s,s:s,s:i,s:s,s:I,s:I,s:I}", "username", 
+                                            shmptr->client[cc].username, 
+                                            "ipaddress", 
+                                            shmptr->client[cc].ipaddress,
+                                            "udp_port",
+                                            shmptr->client[cc].udp_port,
+                                            "channel",
+                                            shmptr->client[cc].channel,
+                                            "lastactivity",
+                                            shmptr->client[cc].lastActivity,
+                                            "connecttime",
+                                            shmptr->client[cc].connectTime,
+                                            "idleTime",
+                                            (long long unsigned int)shmptr->client[cc].lastActivity - shmptr->client[cc].idleTime                                            
+                                            );                                                                                                                                                   
+          json_array_append_new( jarr1, client );           
+          } 
+ json_object_set_new( clients, "clients", jarr1 );         
+ json_object_update( root, clients );
+}
+  json_dump_file(root, "json.txt", JSON_PRESERVE_ORDER | JSON_INDENT(4) );        
+  result = json_dumps(root, JSON_PRESERVE_ORDER | JSON_COMPACT );
+
+  *n = sprintf( (char *)&buf[LWS_SEND_BUFFER_PRE_PADDING], "%s", result  );
+     
+
+  if( result )
+    free( result );
+
+  json_decref(root);
+  return 0;         
+}
+
+struct per_session_data__umurmur_json {
+       int test;
+};
+
+static int
+callback_umurmur_json( struct libwebsocket_context *context,
+                                        struct libwebsocket *wsi,
+                                        enum libwebsocket_callback_reasons reason,
+                                                    void *user, void *in, size_t len)
+{
+       int m, n;
+
+       unsigned char buf[LWS_SEND_BUFFER_PRE_PADDING + 4096 +
+                                                       LWS_SEND_BUFFER_POST_PADDING];
+       
+       //struct per_session_data__umurmur_json *pss = (struct per_session_data__umurmur_json *)user;
+
+       switch (reason) {
+
+       case LWS_CALLBACK_ESTABLISHED:
+               lwsl_info("callback_umurmur_json: LWS_CALLBACK_ESTABLISHED\n");            
+               break;
+
+       case LWS_CALLBACK_SERVER_WRITEABLE:
+    getJsonData( buf, &n );
+    m = libwebsocket_write(wsi, &buf[LWS_SEND_BUFFER_PRE_PADDING], n, LWS_WRITE_TEXT);  //printf("N: %d M: %d\n", n, m );
+
+    if( m == n )
+        return 1;
+               break;
+
+       case LWS_CALLBACK_RECEIVE:
+       //fprintf(stderr, "rx %d\n", (int)len);
+               if (len < 6)
+                       break;
+               if (strcmp((const char *)in, "update\n") == 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:
+               dump_handshake_info(wsi);
+               /* 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 */
+               sizeof (struct per_session_data__http), /* per_session_data_size */
+               0,                                                      /* max frame size / rx buffer */
+       },
+       {
+               "umurmur-json-protocol",
+               callback_umurmur_json,
+               sizeof(struct per_session_data__umurmur_json),
+               128,
+       },
+       { NULL, NULL, 0, 0 } /* terminator */
+};
+
+void sighandler(int sig)
+{
+       force_exit = 1;
+}
+
+static struct option options[] = {
+       { "help",       no_argument,            NULL, 'h' },
+       { "debug",      required_argument,      NULL, 'd' },
+       { "port",       required_argument,      NULL, 'p' },
+       { "ssl",        no_argument,            NULL, 's' },
+       { "interface",  required_argument,      NULL, 'i' },
+       { "daemonize",  no_argument,            NULL, 'D' },
+       { "resource_path", required_argument,           NULL, 'r' },
+       { NULL, 0, 0, 0 }
+};
+
+int main(int argc, char **argv)
+{
+       char cert_path[1024];
+       char key_path[1024];
+       int n = 0;
+       int use_ssl = 0;
+       struct libwebsocket_context *context;
+       int opts = 0;
+       char interface_name[128] = "";
+       const char *iface = NULL;
+//     unsigned int oldus = 0;
+       struct lws_context_creation_info info;
+
+  int syslog_options = LOG_PID | LOG_PERROR; 
+
+       int debug_level = 7;
+
+       int daemonize = 0;
+
+
+       memset(&info, 0, sizeof info);
+       info.port = 7681;
+
+       while (n >= 0) {
+               n = getopt_long(argc, argv, "i:hsp:d:Dr:", options, NULL);
+               if (n < 0)
+                       continue;
+               switch (n) {
+               case 'D':
+                       daemonize = 1;
+                       syslog_options &= ~LOG_PERROR;
+                       break;
+
+               case 'd':
+                       debug_level = atoi(optarg);
+                       break;
+               case 's':
+                       use_ssl = 1;
+                       break;
+               case 'p':
+                       info.port = atoi(optarg);
+                       break;
+               case 'i':
+                       strncpy(interface_name, optarg, sizeof interface_name);
+                       interface_name[(sizeof interface_name) - 1] = '\0';
+                       iface = interface_name;
+                       break;
+               case 'r':
+                       resource_path = optarg;
+                       printf("Setting resource path to \"%s\"\n", resource_path);
+                       break;
+               case 'h':
+                       fprintf(stderr, "Usage: test-server "
+                                       "[--port=<p>] [--ssl] "
+                                       "[-d <log bitfield>] "
+                                       "[--resource_path <path>]\n");
+                       exit(1);
+               }
+       }
+
+key_t key = 0x53021d79;
+
+                    if( ( shmid = shmget( key, 0, 0) ) == -1 )
+                    {
+                        perror("shmget");
+                        printf( "umurmurd doesn't seem to be running\n\r" );                        
+                        exit(EXIT_FAILURE);
+                    }
+
+                    
+                    if( ( shmptr = shmat( shmid,0, 0 ) ) == (void *) -1 )   
+                    {
+                        perror("shmat");
+                        exit(EXIT_FAILURE);
+                    }
+
+
+       /* 
+        * normally lock path would be /var/lock/lwsts or similar, to
+        * simplify getting started without having to take care about
+        * permissions or running as root, set to /tmp/.lwsts-lock
+        */
+       if (daemonize && lws_daemonize("/tmp/.lwsts-lock")) {
+               fprintf(stderr, "Failed to daemonize\n");
+               return 1;
+       }
+
+
+       signal(SIGINT, sighandler);
+
+
+       /* we will only try to log things according to our debug_level */
+       setlogmask(LOG_UPTO (LOG_DEBUG));
+       openlog("lwsts", syslog_options, LOG_DAEMON);
+
+
+       /* tell the library what debug level to emit and to send it to syslog */
+       lws_set_log_level(debug_level, lwsl_emit_syslog);
+
+       lwsl_notice("uMurmurd Websocket server - "
+                       "(C) Copyright 2014 Michael J. Pounders <> - "
+                                                   "licensed under LGPL2.1\n");
+
+       info.iface = iface;
+       info.protocols = protocols;
+
+       info.extensions = libwebsocket_get_internal_extensions();
+
+       if (!use_ssl) {
+               info.ssl_cert_filepath = NULL;
+               info.ssl_private_key_filepath = NULL;
+       } else {
+               if (strlen(resource_path) > sizeof(cert_path) - 32) {
+                       lwsl_err("resource path too long\n");
+                       return -1;
+               }
+               sprintf(cert_path, "%s/ssl/umurmurd_websocket.pem",
+                                                               resource_path);
+               if (strlen(resource_path) > sizeof(key_path) - 32) {
+                       lwsl_err("resource path too long\n");
+                       return -1;
+               }
+               sprintf(key_path, "%s/ssl/umurmurd_websocket.key.pem",
+                                                               resource_path);
+
+               info.ssl_cert_filepath = cert_path;
+               info.ssl_private_key_filepath = key_path;
+       }
+       info.gid = -1;
+       info.uid = -1;
+       info.options = opts;
+
+       context = libwebsocket_create_context(&info);
+       if (context == NULL) {
+               lwsl_err("libwebsocket init failed\n");
+               return -1;
+       }
+
+       n = 0;
+       while (n >= 0 && !force_exit) {
+               struct timeval tv;
+
+               gettimeofday(&tv, NULL);
+
+               /*
+                * This provokes the LWS_CALLBACK_SERVER_WRITEABLE for every
+                * live websocket connection using the DUMB_INCREMENT protocol,
+                * as soon as it can take more packets (usually immediately)
+                */
+
+//             if (((unsigned int)tv.tv_usec - oldus) > 50000) {
+//                     libwebsocket_callback_on_writable_all_protocol(&protocols[PROTOCOL_JSON_UMURMURD]);
+//       oldus = tv.tv_usec;
+//             }
+
+
+               /*
+                * If libwebsockets sockets are all we care about,
+                * you can use this api which takes care of the poll()
+                * and looping through finding who needed service.
+                *
+                * If no socket needs service, it'll return anyway after
+                * the number of ms in the second argument.
+                */
+
+               n = libwebsocket_service(context, 50);
+
+       }
+
+
+       libwebsocket_context_destroy(context);
+
+       lwsl_notice("umurmur_websocket server exited cleanly\n");
+
+  shmdt( shmptr );
+       closelog();
+
+       return 0;
+}