浏览代码

http server and message passer to passenger interface

* still a prototype
* hard coded values
* not ready for production, still testing
clementinecomputing 4 年之前
父节点
当前提交
365e93ee50

+ 7 - 0
.gitignore

@@ -2,6 +2,8 @@
 *.swp
 .bak
 
+.tmp
+
 avls
 diu_minder
 client_supervisor
@@ -62,3 +64,8 @@ server/diu_packager/package/*
 busunit/helper/gpsdist
 busunit/debug/passdb_inspect
 busunit/debug/billdb_inspect
+
+busunit-PIU/piumsgd/piumsgd
+busunit-PIU/piumsgd/client/piumsg
+
+

+ 20 - 0
busunit-PIU/piumsgd/Makefile

@@ -0,0 +1,20 @@
+INSTALLDIR=/home/bus/bin
+
+src = $(wildcard *.cpp *c)
+hdr = $(wildcard *.h *.hpp)
+
+CC=g++
+COPT=-g --std=c++11 -I. -DMG_ENABLE_LOG=0
+
+piumsgd: $(src) $(hdr)
+	$(CC) $(COPT) -o $@ $^
+
+.PHONY: clean
+clean:
+	rm -f *.o piumsgd
+
+.PHONY: install
+install: piumsgd
+	mkdir -p $(INSTALLDIR)
+	cp $< $(INSTALLDIR)
+

+ 20 - 0
busunit-PIU/piumsgd/client/Makefile

@@ -0,0 +1,20 @@
+INSTALLDIR=/home/bus/bin
+
+src = $(wildcard *.cpp *c ../mongoose.c)
+hdr = $(wildcard *.h *.hpp)
+
+CC=g++
+COPT=-g --std=c++11 -I. -I.. -DMG_ENABLE_LOG=0
+
+piumsg: $(src) $(hdr)
+	$(CC) $(COPT) -o $@ $^
+
+.PHONY: clean
+clean:
+	rm -f *.o piumsg
+
+.PHONY: install
+install: piumsg
+	mkdir -p $(INSTALLDIR)
+	cp $< $(INSTALLDIR)
+

+ 191 - 0
busunit-PIU/piumsgd/client/piumsg.c

@@ -0,0 +1,191 @@
+/*
+ * Copyright (c) 2021 Clementine Computing LLC.
+ *
+ * This file is part of PopuFare.
+ *
+ * PopuFare is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * PopuFare 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 Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with PopuFare.  If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+/*
+ * A simple client to inject websocket messages to piumsgd
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <getopt.h>
+
+#include <sysexits.h>
+
+#include "mongoose.h"
+
+#define PIUMSG "piumsg"
+
+#define PIUMSG_VERSION "0.1.0"
+#define PIUMSG_HOST "localhost"
+
+//#define PIUMSG_HOST "ws://192.168.0.26"
+#define PIUMSG_PORT 8001
+
+#define _BUFLEN 1024
+
+char *g_host = NULL;
+int g_port = PIUMSG_PORT;
+char *g_msg = NULL;
+
+//static const char *s_url = "ws://" PIUMSG_HOST  ":" PIUMSG_PORT;
+//
+char *s_url=NULL;
+
+int g_verbose = 0;
+
+struct option g_longopt[] = {
+  { "verbose", no_argument, 0, 'V'},
+  { "version", no_argument, 0, 'v'},
+  { "help", no_argument, 0, 'h'},
+  {0,0,0,0}
+};
+
+// Print websocket response and signal that we're done
+//
+static void _ws(struct mg_connection *c, int ev, void *ev_data, void *_data) {
+  size_t n;
+  int *idat=NULL;
+
+  idat = (int *)_data;
+
+  if (ev == MG_EV_ERROR) {
+
+    // On error, log error message
+    //
+    LOG(LL_ERROR, ("%p %s", c->fd, (char *) ev_data));
+
+  } else if (ev == MG_EV_WS_OPEN) {
+
+    // When websocket handshake is successful, send message
+    //
+    n = strlen(g_msg);
+    printf("# sending: "); fflush(stdout);
+    write(1, g_msg, n);
+    printf("\n"); fflush(stdout);
+    mg_ws_send(c, g_msg, n, WEBSOCKET_OP_TEXT);
+
+  } else if (ev == MG_EV_WS_MSG) {
+
+    // When we get echo response, print it
+    //
+    struct mg_ws_message *wm = (struct mg_ws_message *) ev_data;
+    printf("# got: %.*s\n", (int) wm->data.len, wm->data.ptr);
+
+  }
+
+  if ((ev == MG_EV_ERROR) ||
+      (ev == MG_EV_CLOSE) ||
+      (ev == MG_EV_WS_MSG)) {
+
+    printf("# ev %i (err%i, close%i, msg%i)\n",
+        ev, MG_EV_ERROR, MG_EV_CLOSE, MG_EV_WS_MSG);
+
+    // Signal that we're done
+    //
+    //*(bool *) fn_data = true;
+    *idat = 1;
+  }
+
+}
+
+void show_version(FILE *fp) {
+  fprintf(fp, "version %s\n", PIUMSG_VERSION);
+}
+
+void show_help(FILE *fp) {
+  fprintf(fp, "%s - connect to PIU display manager\n",  PIUMSG);
+  fprintf(fp, "\n");
+  fprintf(fp, "  usage: %s [-h] [-v] [-V] [msg]\n", PIUMSG);
+  fprintf(fp, "\n");
+  fprintf(fp, "  msg        push message\n");
+  fprintf(fp, "  [-H host]  host (default %s)\n", PIUMSG_HOST);
+  fprintf(fp, "  [-p port]  port (default %i)\n", PIUMSG_PORT);
+  fprintf(fp, "  [-V]       verbose\n");
+  fprintf(fp, "  [-h]       help (this screen)\n");
+  fprintf(fp, "  [-v]       print version\n");
+}
+
+int main(int argc, char **argv) {
+  struct mg_mgr mgr;
+  //bool done = false;
+  int done = 0;
+  struct mg_connection *c;
+
+  int ch, option_index;
+
+  s_url = (char *)malloc(sizeof(char)*_BUFLEN);
+
+  while ((ch=getopt_long(argc, argv, "hvVm:H:p:", g_longopt, &option_index)) >= 0) {
+    switch (ch) {
+      case 0:
+        break;
+      case 'H':
+        g_host = strdup(optarg);
+        break;
+      case 'p':
+        g_port = atoi(optarg);
+        break;
+      case 'V':
+        g_verbose++;
+        break;
+      case 'v':
+        show_version(stdout);
+        break;
+      case 'h':
+        show_help(stdout);
+        exit(-1);
+      default:
+        break;
+    }
+  }
+
+  if (optind < argc) {
+    g_msg = strdup(argv[optind]);
+  }
+
+  if (!g_msg) {
+    fprintf(stderr, "provide message\n");
+    show_help(stderr);
+    exit(EX_NOINPUT);
+  }
+
+  if (!g_host) {
+    g_host = strdup(PIUMSG_HOST);
+  }
+
+  snprintf(s_url, _BUFLEN-1, "ws://%s:%i", g_host, g_port);
+  s_url[_BUFLEN-1]=0;
+
+  mg_mgr_init(&mgr);
+  c = mg_ws_connect(&mgr, s_url, _ws, &done, NULL);
+
+  while (c && (done == 0)) {
+    mg_mgr_poll(&mgr, 1000);
+  }
+
+  mg_mgr_free(&mgr);
+  free(g_msg);
+  free(g_host);
+  return 0;
+}

文件差异内容过多而无法显示
+ 4226 - 0
busunit-PIU/piumsgd/mongoose.c


+ 869 - 0
busunit-PIU/piumsgd/mongoose.h

@@ -0,0 +1,869 @@
+// Copyright (c) 2004-2013 Sergey Lyubka
+// Copyright (c) 2013-2020 Cesanta Software Limited
+// All rights reserved
+//
+// This software is dual-licensed: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License version 2 as
+// published by the Free Software Foundation. For the terms of this
+// license, see <http://www.gnu.org/licenses/>.
+//
+// You are free to use this software under the terms of the GNU General
+// Public License, 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.
+//
+// Alternatively, you can license this software under a commercial
+// license, as set out in <https://www.cesanta.com/license>.
+
+#pragma once
+#define MG_VERSION "7.2"
+
+
+#define MG_ARCH_CUSTOM 0
+#define MG_ARCH_UNIX 1
+#define MG_ARCH_WIN32 2
+#define MG_ARCH_ESP32 3
+#define MG_ARCH_ESP8266 4
+#define MG_ARCH_CC3100 5
+#define MG_ARCH_CC3200 6
+#define MG_ARCH_CC3220 7
+#define MG_ARCH_MSP432 8
+#define MG_ARCH_TM4C129 9
+#define MG_ARCH_MBED 10
+#define MG_ARCH_WINCE 11
+#define MG_ARCH_NXP_LPC 12
+#define MG_ARCH_NXP_KINETIS 13
+#define MG_ARCH_NRF51 14
+#define MG_ARCH_NRF52 15
+#define MG_ARCH_PIC32 16
+#define MG_ARCH_RS14100 17
+#define MG_ARCH_STM32 18
+#define MG_ARCH_FREERTOS 19
+
+#if !defined(MG_ARCH)
+#if defined(TARGET_IS_MSP432P4XX) || defined(__MSP432P401R__)
+#define MG_ARCH MG_ARCH_MSP432
+#elif defined(cc3200) || defined(TARGET_IS_CC3200)
+#define MG_ARCH MG_ARCH_CC3200
+#elif defined(cc3220) || defined(TARGET_IS_CC3220)
+#define MG_ARCH MG_ARCH_CC3220
+#elif defined(__unix__) || defined(__APPLE__)
+#define MG_ARCH MG_ARCH_UNIX
+#elif defined(WINCE)
+#define MG_ARCH MG_ARCH_WINCE
+#elif defined(_WIN32)
+#define MG_ARCH MG_ARCH_WIN32
+#elif defined(__MBED__)
+#define MG_ARCH MG_ARCH_MBED
+#elif defined(__USE_LPCOPEN)
+#define MG_ARCH MG_ARCH_NXP_LPC
+#elif defined(FRDM_K64F) || defined(FREEDOM)
+#define MG_ARCH MG_ARCH_NXP_KINETIS
+#elif defined(PIC32)
+#define MG_ARCH MG_ARCH_PIC32
+#elif defined(ICACHE_FLASH) || defined(ICACHE_RAM_ATTR)
+#define MG_ARCH MG_ARCH_ESP8266
+#elif defined(ESP_PLATFORM)
+#define MG_ARCH MG_ARCH_ESP32
+#elif defined(TARGET_IS_TM4C129_RA0) || defined(TARGET_IS_TM4C129_RA1) || \
+    defined(TARGET_IS_TM4C129_RA2)
+#define MG_ARCH MG_ARCH_TM4C129
+#elif defined(RS14100)
+#define MG_ARCH MG_ARCH_RS14100
+#elif defined(STM32)
+#define MG_ARCH MG_ARCH_STM32
+#endif
+
+#if !defined(MG_ARCH)
+#error "MG_ARCH is not specified and we couldn't guess it."
+#endif
+#endif  // !defined(MG_ARCH)
+
+#define WIN32_LEAN_AND_MEAN
+#define _CRT_SECURE_NO_WARNINGS
+#define _WINSOCK_DEPRECATED_NO_WARNINGS
+
+// Standard C headers
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <time.h>
+
+#if MG_ARCH == MG_ARCH_CUSTOM
+#include <mongoose_custom.h>
+#endif
+
+
+
+
+
+
+
+#if !defined(PRINTF_LIKE)
+#if defined(__GNUC__) || defined(__clang__) || defined(__TI_COMPILER_VERSION__)
+#define PRINTF_LIKE(f, a) __attribute__((format(printf, f, a)))
+#else
+#define PRINTF_LIKE(f, a)
+#endif
+#endif
+
+
+#if MG_ARCH == MG_ARCH_ESP32
+
+#include <dirent.h>
+#include <netdb.h>
+#define MG_DIRSEP '/'
+#define MG_INT64_FMT "%lld"
+
+#endif
+
+
+#if MG_ARCH == MG_ARCH_ESP8266
+
+#include <dirent.h>
+#include <esp_system.h>
+#include <netdb.h>
+#include <stdbool.h>
+#include <sys/time.h>
+#define MG_DIRSEP '/'
+#define MG_INT64_FMT "%lld"
+
+#endif
+
+
+#if MG_ARCH == MG_ARCH_FREERTOS
+#include <FreeRTOS.h>
+
+#include <task.h>
+
+#include <FreeRTOS_IP.h>
+#include <FreeRTOS_Sockets.h>
+#include <stdbool.h>
+
+#define MG_INT64_FMT "%lld"
+#define MG_DIRSEP '/'
+#define IPPROTO_TCP FREERTOS_IPPROTO_TCP
+#define IPPROTO_UDP FREERTOS_IPPROTO_UDP
+#define AF_INET FREERTOS_AF_INET
+#define SOCK_STREAM FREERTOS_SOCK_STREAM
+#define SOCK_DGRAM FREERTOS_SOCK_DGRAM
+#define SO_BROADCAST 0
+#define SO_ERROR 0
+#define SOL_SOCKET 0
+#define SO_REUSEADDR 0
+#define sockaddr_in freertos_sockaddr
+struct sockaddr {
+  uint8_t sa_len, sa_family;
+};
+#define accept(a, b, c) FreeRTOS_accept((a), (b), (c))
+#define connect(a, b, c) FreeRTOS_connect((a), (b), (c))
+#define bind(a, b, c) FreeRTOS_bind((a), (b), (c))
+#define listen(a, b) FreeRTOS_listen((a), (b))
+#define socket(a, b, c) FreeRTOS_socket((a), (b), (c))
+#define send(a, b, c, d) FreeRTOS_send((a), (b), (c), (d))
+#define recv(a, b, c, d) FreeRTOS_recv((a), (b), (c), (d))
+#define setsockopt(a, b, c, d, e) FreeRTOS_setsockopt((a), (b), (c), (d), (e))
+#define sendto(a, b, c, d, e, f) FreeRTOS_sendto((a), (b), (c), (d), (e), (f))
+#define recvfrom(a, b, c, d, e, f) \
+  FreeRTOS_recvfrom((a), (b), (c), (d), (e), (f))
+#define closesocket(x) FreeRTOS_closesocket(x)
+#define gethostbyname(x) FreeRTOS_gethostbyname(x)
+
+#include <ff_stdio.h>
+
+#undef FILE
+#define FILE FF_FILE
+//#define SEEK_SET FF_SEEK_SET
+//#define SEEK_END FF_SEEK_END
+#define stat(a, b) ff_stat((a), (b))
+#define fopen(a, b) ff_fopen((a), (b))
+#define fclose(a) ff_fclose(a)
+#define fread(a, b, c, d) ff_fread((a), (b), (c), (d))
+#define fwrite(a, b, c, d) ff_fwrite((a), (b), (c), (d))
+#define vfprintf ff_vfprintf
+#define fprintf ff_fprintf
+#define remove(a) ff_remove(a)
+#define rename(a, b) ff_rename((a), (b), 1)
+
+static inline int ff_vfprintf(FF_FILE *fp, const char *fmt, va_list ap) {
+  char *buf = NULL;
+  int n = mg_vasprintf(&buf, 0, fmt, ap);
+  if (buf != NULL) ff_fwrite(buf, 1, n, fp), free(buf);
+  return n;
+}
+
+#endif
+
+
+#if MG_ARCH == MG_ARCH_UNIX
+
+#define _DARWIN_UNLIMITED_SELECT 1
+
+#include <arpa/inet.h>
+#include <dirent.h>
+#include <inttypes.h>
+#include <netdb.h>
+#include <netinet/tcp.h>
+#include <signal.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <sys/select.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <unistd.h>
+#define MG_DIRSEP '/'
+#define MG_ENABLE_POSIX 1
+#define MG_INT64_FMT "%" PRId64
+
+#endif
+
+
+#if MG_ARCH == MG_ARCH_WIN32
+
+#if defined(_MSC_VER) && _MSC_VER < 1700
+#define __func__ ""
+typedef __int64 int64_t;
+typedef unsigned __int64 uint64_t;
+typedef unsigned char uint8_t;
+typedef char int8_t;
+typedef unsigned short uint16_t;
+typedef short int16_t;
+typedef unsigned int uint32_t;
+typedef int int32_t;
+typedef enum { false = 0, true = 1 } bool;
+#else
+#include <stdbool.h>
+#include <stdint.h>
+#include <ws2tcpip.h>
+#endif
+
+#include <winsock2.h>
+
+// Protect from calls like std::snprintf in app code
+// See https://github.com/cesanta/mongoose/issues/1047
+#ifndef __cplusplus
+#define snprintf _snprintf
+#define vsnprintf _vsnprintf
+#define strdup(x) _strdup(x)
+#endif
+
+typedef int socklen_t;
+#define MG_DIRSEP '\\'
+#ifndef PATH_MAX
+#define PATH_MAX MAX_PATH
+#endif
+#ifndef EINPROGRESS
+#define EINPROGRESS WSAEINPROGRESS
+#endif
+#ifndef EWOULDBLOCK
+#define EWOULDBLOCK WSAEWOULDBLOCK
+#endif
+#define realpath(a, b) _fullpath((b), (a), 512)
+#ifndef va_copy
+#ifdef __va_copy
+#define va_copy __va_copy
+#else
+#define va_copy(x, y) (x) = (y)
+#endif
+#endif
+#ifndef S_ISDIR
+#define S_ISDIR(x) (((x) &_S_IFMT) == _S_IFDIR)
+#endif
+
+#define MG_INT64_FMT "%I64d"
+
+#endif
+
+
+#ifndef MG_ENABLE_LWIP
+#define MG_ENABLE_LWIP 0
+#endif
+
+#if MG_ENABLE_LWIP
+#define MG_ENABLE_SOCKET 0
+#elif !defined(MG_ENABLE_SOCKET)
+#define MG_ENABLE_SOCKET 1
+#endif
+
+#ifndef MG_ENABLE_MBEDTLS
+#define MG_ENABLE_MBEDTLS 0
+#endif
+
+#ifndef MG_ENABLE_OPENSSL
+#define MG_ENABLE_OPENSSL 0
+#endif
+
+#ifndef MG_ENABLE_FS
+#define MG_ENABLE_FS 1
+#endif
+
+#ifndef MG_ENABLE_SSI
+#define MG_ENABLE_SSI 0
+#endif
+
+#ifndef MG_ENABLE_IPV6
+#define MG_ENABLE_IPV6 0
+#endif
+
+#ifndef MG_ENABLE_LOG
+#define MG_ENABLE_LOG 1
+#endif
+
+#ifndef MG_ENABLE_MGOS
+#define MG_ENABLE_MGOS 0
+#endif
+
+#ifndef MG_ENABLE_MD5
+#define MG_ENABLE_MD5 0
+#endif
+
+#ifndef MG_ENABLE_DIRECTORY_LISTING
+#define MG_ENABLE_DIRECTORY_LISTING 0
+#endif
+
+#ifndef MG_ENABLE_HTTP_DEBUG_ENDPOINT
+#define MG_ENABLE_HTTP_DEBUG_ENDPOINT 0
+#endif
+
+#ifndef MG_ENABLE_SOCKETPAIR
+#define MG_ENABLE_SOCKETPAIR 0
+#endif
+
+// Granularity of the send/recv IO buffer growth
+#ifndef MG_IO_SIZE
+#define MG_IO_SIZE 512
+#endif
+
+// Maximum size of the recv IO buffer
+#ifndef MG_MAX_RECV_BUF_SIZE
+#define MG_MAX_RECV_BUF_SIZE (3 * 1024 * 1024)
+#endif
+
+#ifndef MG_MAX_HTTP_HEADERS
+#define MG_MAX_HTTP_HEADERS 40
+#endif
+
+
+#include <ctype.h>
+#include <string.h>
+
+struct mg_str {
+  const char *ptr;
+  size_t len;
+};
+
+#define MG_NULL_STR \
+  { NULL, 0 }
+
+struct mg_str mg_str(const char *s);
+struct mg_str mg_str_n(const char *s, size_t n);
+int mg_lower(const char *s);
+int mg_ncasecmp(const char *s1, const char *s2, size_t len);
+int mg_casecmp(const char *s1, const char *s2);
+int mg_vcmp(const struct mg_str *s1, const char *s2);
+int mg_vcasecmp(const struct mg_str *str1, const char *str2);
+int mg_strcmp(const struct mg_str str1, const struct mg_str str2);
+struct mg_str mg_strstrip(struct mg_str s);
+struct mg_str mg_strdup(const struct mg_str s);
+const char *mg_strstr(const struct mg_str haystack, const struct mg_str needle);
+
+
+
+
+
+#if MG_ENABLE_MGOS
+#include <common/cs_dbg.h>
+#else
+#if MG_ENABLE_LOG
+#define LOG(level, args)                                                   \
+  do {                                                                     \
+    if (mg_log_prefix((level), __FILE__, __LINE__, __func__)) mg_log args; \
+  } while (0)
+enum { LL_NONE, LL_ERROR, LL_INFO, LL_DEBUG, LL_VERBOSE_DEBUG };
+bool mg_log_prefix(int ll, const char *file, int line, const char *fname);
+void mg_log(const char *fmt, ...) PRINTF_LIKE(1, 2);
+void mg_log_set(const char *spec);
+void mg_log_set_callback(void (*fn)(const void *, int, void *), void *param);
+#else
+#define LOG(level, args)
+#endif
+#endif
+
+
+struct mg_timer {
+  int period_ms;            // Timer period in milliseconds
+  int flags;                // Possible flags values below
+#define MG_TIMER_REPEAT 1   // Call function periodically, otherwise run once
+#define MG_TIMER_RUN_NOW 2  // Call immediately when timer is set
+  void (*fn)(void *);       // Function to call
+  void *arg;                // Function agrument
+  unsigned long expire;     // Expiration timestamp in milliseconds
+  struct mg_timer *next;    // Linkage in g_timers list
+};
+
+extern struct mg_timer *g_timers;  // Global list of timers
+
+void mg_timer_init(struct mg_timer *, int ms, int, void (*fn)(void *), void *);
+void mg_timer_free(struct mg_timer *);
+void mg_timer_poll(unsigned long uptime_ms);
+
+
+
+
+
+char *mg_file_read(const char *path);
+size_t mg_file_size(const char *path);
+bool mg_file_write(const char *path, const void *buf, size_t len);
+bool mg_file_printf(const char *path, const char *fmt, ...);
+void mg_random(void *buf, size_t len);
+bool mg_globmatch(const char *pattern, int plen, const char *s, int n);
+bool mg_next_comma_entry(struct mg_str *s, struct mg_str *k, struct mg_str *v);
+uint16_t mg_ntohs(uint16_t net);
+uint32_t mg_ntohl(uint32_t net);
+char *mg_hexdump(const void *buf, size_t len);
+char *mg_hex(const void *buf, int len, char *dst);
+void mg_unhex(const char *buf, int len, unsigned char *to);
+unsigned long mg_unhexn(const char *s, int len);
+int mg_asprintf(char **buf, size_t size, const char *fmt, ...);
+int mg_vasprintf(char **buf, size_t size, const char *fmt, va_list ap);
+int64_t mg_to64(struct mg_str str);
+double mg_time(void);
+unsigned long mg_millis(void);
+void mg_usleep(unsigned long usecs);
+
+#if MG_ENABLE_FS
+#ifdef _WIN32
+typedef struct _stati64 mg_stat_t;
+#define mg_stat(a, b) _stati64((a), (b))
+#else
+typedef struct stat mg_stat_t;
+#define mg_stat(a, b) stat((a), (b))
+#endif
+#endif
+
+#define mg_htons(x) mg_ntohs(x)
+#define mg_htonl(x) mg_ntohl(x)
+
+#define MG_SWAP16(x) ((((x) >> 8) & 255) | ((x & 255) << 8))
+#define MG_SWAP32(x) \
+  (((x) >> 24) | (((x) &0xff0000) >> 8) | (((x) &0xff00) << 8) | ((x) << 24))
+
+#if !defined(WEAK)
+#if (defined(__GNUC__) || defined(__clang__) || \
+     defined(__TI_COMPILER_VERSION__)) &&       \
+    !defined(_WIN32)
+#define WEAK __attribute__((weak))
+#else
+#define WEAK
+#endif
+#endif
+
+// Expands to a string representation of its argument: e.g.
+// MG_STRINGIFY_LITERAL(5) expands to "5"
+#if !defined(_MSC_VER) || _MSC_VER >= 1900
+#define MG_STRINGIFY_LITERAL(...) #__VA_ARGS__
+#else
+#define MG_STRINGIFY_LITERAL(x) #x
+#endif
+
+#define LIST_ADD_HEAD(type_, head_, elem_) \
+  do {                                     \
+    (elem_)->next = (*head_);              \
+    *(head_) = (elem_);                    \
+  } while (0)
+
+#define LIST_ADD_TAIL(type_, head_, elem_) \
+  do {                                     \
+    type_ **h = head_;                     \
+    while (*h != NULL) h = &(*h)->next;    \
+    *h = (elem_);                          \
+  } while (0)
+
+#define LIST_DELETE(type_, head_, elem_)   \
+  do {                                     \
+    type_ **h = head_;                     \
+    while (*h != (elem_)) h = &(*h)->next; \
+    *h = (elem_)->next;                    \
+  } while (0)
+
+// Expands to a string representation of its argument, which can be a macro:
+// #define FOO 123
+// MG_STRINGIFY_MACRO(FOO)  // Expands to 123
+#define MG_STRINGIFY_MACRO(x) MG_STRINGIFY_LITERAL(x)
+
+
+
+unsigned short mg_url_port(const char *url);
+int mg_url_is_ssl(const char *url);
+struct mg_str mg_url_host(const char *url);
+struct mg_str mg_url_user(const char *url);
+struct mg_str mg_url_pass(const char *url);
+const char *mg_url_uri(const char *url);
+
+
+#include <stddef.h>
+
+struct mg_iobuf {
+  unsigned char *buf;
+  size_t size, len;
+};
+
+int mg_iobuf_init(struct mg_iobuf *, size_t);
+int mg_iobuf_resize(struct mg_iobuf *, size_t);
+void mg_iobuf_free(struct mg_iobuf *);
+size_t mg_iobuf_append(struct mg_iobuf *, const void *, size_t, size_t);
+size_t mg_iobuf_delete(struct mg_iobuf *, size_t);
+
+int mg_base64_update(unsigned char p, char *to, int len);
+int mg_base64_final(char *to, int len);
+int mg_base64_encode(const unsigned char *p, int n, char *to);
+int mg_base64_decode(const char *src, int n, char *dst);
+
+
+
+
+typedef struct {
+  uint32_t buf[4];
+  uint32_t bits[2];
+  unsigned char in[64];
+} mg_md5_ctx;
+
+void mg_md5_init(mg_md5_ctx *c);
+void mg_md5_update(mg_md5_ctx *c, const unsigned char *data, size_t len);
+void mg_md5_final(mg_md5_ctx *c, unsigned char[16]);
+
+
+
+
+typedef struct {
+  uint32_t state[5];
+  uint32_t count[2];
+  unsigned char buffer[64];
+} mg_sha1_ctx;
+
+void mg_sha1_init(mg_sha1_ctx *);
+void mg_sha1_update(mg_sha1_ctx *, const unsigned char *data, size_t len);
+void mg_sha1_final(unsigned char digest[20], mg_sha1_ctx *);
+void mg_hmac_sha1(const unsigned char *key, size_t key_len,
+                  const unsigned char *text, size_t text_len,
+                  unsigned char out[20]);
+
+
+struct mg_connection;
+typedef void (*mg_event_handler_t)(struct mg_connection *, int ev,
+                                   void *ev_data, void *fn_data);
+#if 0
+struct mg_fn {
+  struct mg_fn *next;
+  mg_event_handler_t fn;
+  void *fn_data;
+};
+
+void mg_fn_add(struct mg_connection *c, mg_event_handler_t fn, void *fn_data);
+void mg_fn_del(struct mg_connection *c, mg_event_handler_t fn);
+#endif
+
+void mg_call(struct mg_connection *c, int ev, void *ev_data);
+void mg_error(struct mg_connection *c, const char *fmt, ...);
+
+enum {
+  MG_EV_ERROR,      // Error                        char *error_message
+  MG_EV_POLL,       // mg_mgr_poll iteration        unsigned long *millis
+  MG_EV_RESOLVE,    // Host name is resolved        NULL
+  MG_EV_CONNECT,    // Connection established       NULL
+  MG_EV_ACCEPT,     // Connection accepted          NULL
+  MG_EV_READ,       // Data received from socket    struct mg_str *
+  MG_EV_WRITE,      // Data written to socket       int *num_bytes_written
+  MG_EV_CLOSE,      // Connection closed            NULL
+  MG_EV_HTTP_MSG,   // HTTP request/response        struct mg_http_message *
+  MG_EV_WS_OPEN,    // Websocket handshake done     struct mg_http_message *
+  MG_EV_WS_MSG,     // Websocket msg, text or bin   struct mg_ws_message *
+  MG_EV_WS_CTL,     // Websocket control msg        struct mg_ws_message *
+  MG_EV_MQTT_CMD,   // MQTT low-level command       struct mg_mqtt_message *
+  MG_EV_MQTT_MSG,   // MQTT PUBLISH received        struct mg_mqtt_message *
+  MG_EV_MQTT_OPEN,  // MQTT CONNACK received        int *connack_status_code
+  MG_EV_SNTP_TIME,  // SNTP time received           struct timeval *
+  MG_EV_USER,       // Starting ID for user events
+};
+
+
+
+
+
+
+
+struct mg_dns {
+  const char *url;          // DNS server URL
+  struct mg_connection *c;  // DNS server connection
+};
+
+struct mg_addr {
+  uint16_t port;    // TCP or UDP port in network byte order
+  uint32_t ip;      // IP address in network byte order
+  uint8_t ip6[16];  // IPv6 address
+  bool is_ip6;      // True when address is IPv6 address
+};
+
+struct mg_mgr {
+  struct mg_connection *conns;  // List of active connections
+  struct mg_dns dns4;           // DNS for IPv4
+  struct mg_dns dns6;           // DNS for IPv6
+  int dnstimeout;               // DNS resolve timeout in milliseconds
+  unsigned long nextid;         // Next connection ID
+#if MG_ARCH == MG_ARCH_FREERTOS
+  SocketSet_t ss;  // NOTE(lsm): referenced from socket struct
+#endif
+};
+
+struct mg_connection {
+  struct mg_connection *next;  // Linkage in struct mg_mgr :: connections
+  struct mg_mgr *mgr;          // Our container
+  struct mg_addr peer;         // Remote peer address
+  void *fd;                    // Connected socket, or LWIP data
+  unsigned long id;            // Auto-incrementing unique connection ID
+  struct mg_iobuf recv;        // Incoming data
+  struct mg_iobuf send;        // Outgoing data
+  mg_event_handler_t fn;       // User-specified event handler function
+  void *fn_data;               // User-speficied function parameter
+  mg_event_handler_t pfn;      // Protocol-specific handler function
+  void *pfn_data;              // Protocol-specific function parameter
+  char label[32];              // Arbitrary label
+  void *tls;                   // TLS specific data
+  unsigned is_listening : 1;   // Listening connection
+  unsigned is_client : 1;      // Outbound (client) connection
+  unsigned is_accepted : 1;    // Accepted (server) connection
+  unsigned is_resolving : 1;   // Non-blocking DNS resolv is in progress
+  unsigned is_connecting : 1;  // Non-blocking connect is in progress
+  unsigned is_tls : 1;         // TLS-enabled connection
+  unsigned is_tls_hs : 1;      // TLS handshake is in progress
+  unsigned is_udp : 1;         // UDP connection
+  unsigned is_websocket : 1;   // WebSocket connection
+  unsigned is_hexdumping : 1;  // Hexdump in/out traffic
+  unsigned is_draining : 1;    // Send remaining data, then close and free
+  unsigned is_closing : 1;     // Close and free the connection immediately
+  unsigned is_readable : 1;    // Connection is ready to read
+  unsigned is_writable : 1;    // Connection is ready to write
+};
+
+void mg_mgr_poll(struct mg_mgr *, int ms);
+void mg_mgr_init(struct mg_mgr *);
+void mg_mgr_free(struct mg_mgr *);
+
+struct mg_connection *mg_listen(struct mg_mgr *, const char *url,
+                                mg_event_handler_t fn, void *fn_data);
+struct mg_connection *mg_connect(struct mg_mgr *, const char *url,
+                                 mg_event_handler_t fn, void *fn_data);
+int mg_send(struct mg_connection *, const void *, size_t);
+int mg_printf(struct mg_connection *, const char *fmt, ...);
+int mg_vprintf(struct mg_connection *, const char *fmt, va_list ap);
+char *mg_straddr(struct mg_connection *, char *, size_t);
+bool mg_socketpair(int *s1, int *s2);
+bool mg_aton(struct mg_str str, struct mg_addr *addr);
+char *mg_ntoa(const struct mg_addr *addr, char *buf, size_t len);
+
+
+
+
+
+
+struct mg_http_header {
+  struct mg_str name;
+  struct mg_str value;
+};
+
+struct mg_http_message {
+  //        GET /foo/bar/baz?aa=b&cc=ddd HTTP/1.1
+  // method |-| |----uri---| |--query--| |proto-|
+
+  struct mg_str method, uri, query, proto;             // Request/response line
+  struct mg_http_header headers[MG_MAX_HTTP_HEADERS];  // Headers
+  struct mg_str body;                                  // Body
+  struct mg_str message;  // Request line + headers + body
+};
+
+// Parameter for mg_http_serve_dir()
+struct mg_http_serve_opts {
+  const char *root_dir;     // Web root directory, must be non-NULL
+  const char *ssi_pattern;  // SSI filename pattern, e.g. #.shtml
+};
+
+int mg_http_parse(const char *s, size_t len, struct mg_http_message *);
+int mg_http_get_request_len(const unsigned char *buf, size_t buf_len);
+void mg_http_printf_chunk(struct mg_connection *cnn, const char *fmt, ...);
+void mg_http_write_chunk(struct mg_connection *c, const char *buf, size_t len);
+struct mg_connection *mg_http_listen(struct mg_mgr *, const char *url,
+                                     mg_event_handler_t fn, void *fn_data);
+struct mg_connection *mg_http_connect(struct mg_mgr *, const char *url,
+                                      mg_event_handler_t fn, void *fn_data);
+void mg_http_serve_dir(struct mg_connection *, struct mg_http_message *hm,
+                       struct mg_http_serve_opts *);
+void mg_http_serve_file(struct mg_connection *, struct mg_http_message *,
+                        const char *, const char *mime, const char *headers);
+void mg_http_reply(struct mg_connection *, int status_code, const char *headers,
+                   const char *body_fmt, ...);
+struct mg_str *mg_http_get_header(struct mg_http_message *, const char *name);
+void mg_http_event_handler(struct mg_connection *c, int ev);
+int mg_http_get_var(const struct mg_str *, const char *name, char *, int);
+int mg_url_decode(const char *s, size_t n, char *to, size_t to_len, int form);
+void mg_http_creds(struct mg_http_message *, char *user, int, char *pass, int);
+bool mg_http_match_uri(const struct mg_http_message *, const char *glob);
+int mg_http_upload(struct mg_connection *, struct mg_http_message *hm,
+                   const char *dir);
+void mg_http_bauth(struct mg_connection *, const char *user, const char *pass);
+
+
+void mg_http_serve_ssi(struct mg_connection *c, const char *root,
+                       const char *fullpath);
+
+
+
+struct mg_tls_opts {
+  const char *ca;        // CA certificate file. For both listeners and clients
+  const char *cert;      // Certificate
+  const char *certkey;   // Certificate key
+  const char *ciphers;   // Cipher list
+  const char *srvname;   // If not NULL, enables server name verification
+};
+
+int mg_tls_init(struct mg_connection *, struct mg_tls_opts *);
+int mg_tls_free(struct mg_connection *);
+int mg_tls_send(struct mg_connection *, const void *buf, size_t len, int *fail);
+int mg_tls_recv(struct mg_connection *, void *buf, size_t len, int *fail);
+int mg_tls_handshake(struct mg_connection *);
+
+
+#define WEBSOCKET_OP_CONTINUE 0
+#define WEBSOCKET_OP_TEXT 1
+#define WEBSOCKET_OP_BINARY 2
+#define WEBSOCKET_OP_CLOSE 8
+#define WEBSOCKET_OP_PING 9
+#define WEBSOCKET_OP_PONG 10
+
+#define WEBSOCKET_FLAGS_MASK_FIN 128
+#define WEBSOCKET_FLAGS_MASK_OP 15
+
+
+
+struct mg_ws_message {
+  struct mg_str data;
+  uint8_t flags;  // Websocket message flags
+};
+
+struct mg_connection *mg_ws_connect(struct mg_mgr *, const char *url,
+                                    mg_event_handler_t fn, void *fn_data,
+                                    const char *fmt, ...);
+void mg_ws_upgrade(struct mg_connection *, struct mg_http_message *,
+                   const char *fmt, ...);
+size_t mg_ws_send(struct mg_connection *, const char *buf, size_t len, int op);
+
+
+
+
+struct mg_connection *mg_sntp_connect(struct mg_mgr *mgr, const char *url,
+                                      mg_event_handler_t fn, void *fn_data);
+void mg_sntp_send(struct mg_connection *c, unsigned long utc);
+int mg_sntp_parse(const unsigned char *buf, size_t len, struct timeval *tv);
+
+
+
+
+
+#define MQTT_CMD_CONNECT 1
+#define MQTT_CMD_CONNACK 2
+#define MQTT_CMD_PUBLISH 3
+#define MQTT_CMD_PUBACK 4
+#define MQTT_CMD_PUBREC 5
+#define MQTT_CMD_PUBREL 6
+#define MQTT_CMD_PUBCOMP 7
+#define MQTT_CMD_SUBSCRIBE 8
+#define MQTT_CMD_SUBACK 9
+#define MQTT_CMD_UNSUBSCRIBE 10
+#define MQTT_CMD_UNSUBACK 11
+#define MQTT_CMD_PINGREQ 12
+#define MQTT_CMD_PINGRESP 13
+#define MQTT_CMD_DISCONNECT 14
+
+#define MQTT_QOS(qos) ((qos) << 1)
+#define MQTT_GET_QOS(flags) (((flags) &0x6) >> 1)
+#define MQTT_SET_QOS(flags, qos) (flags) = ((flags) & ~0x6) | ((qos) << 1)
+
+struct mg_mqtt_opts {
+  struct mg_str client_id;
+  struct mg_str will_topic;
+  struct mg_str will_message;
+  uint8_t qos;         // Quality of service
+  bool will_retain;    // Retain last will
+  bool clean;          // Use clean session, 0 or 1
+  uint16_t keepalive;  // Keep-alive timer in seconds
+};
+
+struct mg_mqtt_message {
+  struct mg_str topic;    // Parsed topic
+  struct mg_str data;     // Parsed message
+  struct mg_str dgram;    // Whole MQTT datagram, including headers
+  uint16_t id;  // Set for PUBACK, PUBREC, PUBREL, PUBCOMP, SUBACK, PUBLISH
+  uint8_t cmd;  // MQTT command, one of MQTT_CMD_*
+  uint8_t qos;  // Quality of service
+  uint8_t ack;  // Connack return code. 0 - success
+};
+
+struct mg_connection *mg_mqtt_connect(struct mg_mgr *, const char *url,
+                                      struct mg_mqtt_opts *opts,
+                                      mg_event_handler_t fn, void *fn_data);
+struct mg_connection *mg_mqtt_listen(struct mg_mgr *mgr, const char *url,
+                                     mg_event_handler_t fn, void *fn_data);
+void mg_mqtt_pub(struct mg_connection *, struct mg_str *topic,
+                 struct mg_str *data);
+void mg_mqtt_sub(struct mg_connection *, struct mg_str *topic);
+int mg_mqtt_parse(const uint8_t *buf, size_t len, struct mg_mqtt_message *m);
+void mg_mqtt_send_header(struct mg_connection *, uint8_t cmd, uint8_t flags,
+                         uint32_t len);
+int mg_mqtt_next_sub(struct mg_mqtt_message *msg, struct mg_str *topic,
+                     uint8_t *qos, int pos);
+
+
+
+
+
+// Mongoose sends DNS queries that contain only one question:
+// either A (IPv4) or AAAA (IPv6) address lookup.
+// Therefore, we expect zero or one answer.
+// If `resolved` is true, then `addr` contains resolved IPv4 or IPV6 address.
+struct mg_dns_message {
+  uint16_t txnid;       // Transaction ID
+  bool resolved;        // Resolve successful, addr is set
+  struct mg_addr addr;  // Resolved address
+  char name[256];       // Host name
+};
+
+struct mg_dns_header {
+  uint16_t txnid;  // Transaction ID
+  uint16_t flags;
+  uint16_t num_questions;
+  uint16_t num_answers;
+  uint16_t num_authority_prs;
+  uint16_t num_other_prs;
+};
+
+// DNS resource record
+struct mg_dns_rr {
+  uint16_t nlen;    // Name or pointer length
+  uint16_t atype;   // Address type
+  uint16_t aclass;  // Address class
+  uint16_t alen;    // Address length
+};
+
+void mg_resolve(struct mg_connection *, struct mg_str *, int);
+void mg_resolve_cancel(struct mg_connection *);
+bool mg_dns_parse(const uint8_t *buf, size_t len, struct mg_dns_message *);
+size_t mg_dns_parse_rr(const uint8_t *buf, size_t len, size_t ofs,
+                       bool is_question, struct mg_dns_rr *);
+size_t mg_dns_decode_name(const uint8_t *, size_t, size_t, char *, size_t);

+ 198 - 0
busunit-PIU/piumsgd/piumsgd.c

@@ -0,0 +1,198 @@
+/*
+ * Copyright (c) 2021 Clementine Computing LLC.
+ *
+ * This file is part of PopuFare.
+ *
+ * PopuFare is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * PopuFare 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 Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with PopuFare.  If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+/*
+ * PIU interface server.
+ * piumsgd is in charge of passing messages from the underlying system
+ * to the passenger via a websocket connection.
+ *
+ * piumsgd also acts as the web server to serve the local passenger
+ * interface HTML/CSS/JS files.
+
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <getopt.h>
+#include <string.h>
+
+#include "mongoose.h"
+
+struct mg_mgr g_mgr;
+
+char s_web_directory[] = ".";
+
+/*
+void ws_send(struct mg_mgr *mgr, char *msg) {
+  struct mg_connection *conn;
+  for (conn = mg_next(mgr, NULL); conn; conn = mg_next(mgr, conn)) {
+    if (!is_websocket(conn)) { continue; }
+    mg_send_websocket_frame(conn, WEBSOCKET_OP_TEXT, msg, strlen(msg));
+  }
+}
+*/
+
+void _ws_send(struct mg_mgr *mgr, char *msg, size_t msg_n) {
+  int i;
+  struct mg_connection *con;
+
+  printf("  ... ");
+  for (i=0; i<msg_n; i++) { printf("%c", msg[i]); }
+  printf("\n");
+
+  for (con = mgr->conns; con; con = con->next) {
+    printf("  ...con %i %i\n", con->is_websocket, con->is_client);
+    //if (con->is_websocket && con->is_client) {
+    if (con->is_websocket && con->is_accepted) {
+      printf("    !!\n");
+
+      mg_ws_send(con, msg, msg_n, WEBSOCKET_OP_TEXT);
+    }
+  }
+}
+
+/*
+void _ws_send_str(struct mg_connection *mgc, char *msg) {
+  size_t msg_n;
+  msg_n = strlen(msg);
+  _ws_send(mgc, msg, msg_n);
+}
+*/
+
+
+static void _cb(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
+  size_t n;
+
+  if (ev == MG_EV_HTTP_MSG) {
+
+    printf("bang\n"); fflush(stdout);
+
+    struct mg_http_message *hm = (struct mg_http_message *) ev_data;
+    if (mg_http_match_uri(hm, "/websocket")) {
+
+      // Upgrade to websocket. From now on, a connection is a full-duplex
+      // Websocket connection, which will receive MG_EV_WS_MSG events.
+      //
+      printf("ws request?\n"); fflush(stdout);
+
+      mg_ws_upgrade(c, hm, NULL);
+    }
+    else if (mg_http_match_uri(hm, "/")) {
+
+      printf("ws request? (1)\n"); fflush(stdout);
+      mg_ws_upgrade(c, hm, NULL);
+    }
+    else if (mg_http_match_uri(hm, "/rest")) {
+
+      // Serve REST response
+      //
+      mg_http_reply(c, 200, "", "{\"result\": %d}\n", 123);
+    }
+    else {
+
+      // Serve static files
+      //
+      struct mg_http_serve_opts opts = {.root_dir = s_web_directory};
+      mg_http_serve_dir(c, (mg_http_message *)ev_data, &opts);
+    }
+  } else if (ev == MG_EV_WS_MSG) {
+
+    // Got websocket frame. Received data is wm->data. Echo it back!
+    //
+    struct mg_ws_message *wm = (struct mg_ws_message *) ev_data;
+
+    printf(">>> ");
+    for (int i=0; i<wm->data.len; i++) {
+      printf("%c", *(wm->data.ptr + i));
+    }
+    printf("\n"); fflush(stdout);
+
+    n = strlen("relay ");
+    if (wm->data.len < n) {
+      n = wm->data.len;
+    }
+
+    if (n < wm->data.len) {
+      if (strncmp("relay ", (char *)(wm->data.ptr), n)==0) {
+        _ws_send(&g_mgr, ((char *)(wm->data.ptr)) + n, wm->data.len - n);
+      }
+    }
+
+    mg_ws_send(c, wm->data.ptr, wm->data.len, WEBSOCKET_OP_TEXT);
+    mg_iobuf_delete(&c->recv, c->recv.len);
+
+    printf("broadcasting...\n");
+    n = strlen("broadcast");
+    _ws_send(&g_mgr, (char *)"broadcast", n);
+
+  }
+  (void) fn_data;
+}
+
+int main(int argc, char **argv) {
+
+  //struct pollfd fds[32];
+  int nfd=0;
+
+  int poll_return;
+  int read_return;
+
+  time_t now;
+
+  struct mg_conneciton *nc;
+
+  // setup mongoose web server
+  //
+  mg_mgr_init(&g_mgr);
+  mg_http_listen(&g_mgr, "http://127.0.0.1:8001", _cb, NULL);
+  for (;;) {
+    mg_mgr_poll(&g_mgr, 200);
+  }
+  mg_mgr_free(&g_mgr);
+
+  exit(0);
+  /*
+
+  //loop until we get asked to exit.../
+  //
+  while (1) {
+
+
+    // Add our websocket socket to the list of file descriptors
+    // to select on.
+    //
+    for (nc = g_mgr.active_connections; nc != NULL; nc = nc->next) {
+
+      if (nc->sock != INVALID_SOCKET) {
+        if (nfd < 32) {
+          fds[nfd].fd = nc->sock;
+          fds[nfd].events = POLLIN | POLLOUT;
+          fds[nfd].revents = 0;
+          nfd++;
+        }
+      }
+    }
+
+
+  }
+  */
+
+}