/* * 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 . * */ /* * 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 #include #include #include #include #include "mongoose.h" #define PIUMSGD_VERSION "0.1.0" #define PIUMSGD_DEFAULT_WEBDIR "/home/bus/config/html" #define PIUMSGD_DEFAULT_ADDR "0.0.0.0" #define PIUMSGD_DEFAULT_PORT 8001 static struct option long_opt[] = { {"webdir", required_argument, 0, 0}, {"host", required_argument, 0, 0}, {"port", required_argument, 0, 0}, {"help", no_argument, 0, 0}, {"verbose", no_argument, 0, 0}, {"version", no_argument, 0, 0}, {0, 0, 0, 0} }; int g_verbose = 0; //char s_web_directory[] = "/home/bus/config/html"; char *s_web_directory = NULL; struct mg_mgr g_mgr; void _ws_send(struct mg_mgr *mgr, char *msg, size_t msg_n) { int i; struct mg_connection *con; for (con = mgr->conns; con; con = con->next) { if (con->is_websocket && con->is_accepted) { mg_ws_send(con, msg, msg_n, WEBSOCKET_OP_TEXT); } } } static void _cb(struct mg_connection *c, int ev, void *ev_data, void *fn_data) { int i; size_t n; struct mg_http_message *hm = NULL; if (ev == MG_EV_HTTP_MSG) { hm = (struct mg_http_message *) ev_data; if (mg_http_match_uri(hm, "/ws")) { // Upgrade to websocket. From now on, a connection is a full-duplex // Websocket connection, which will receive MG_EV_WS_MSG events. // mg_ws_upgrade(c, hm, NULL); } else if (mg_http_match_uri(hm, "/")) { struct mg_http_serve_opts opts = {.root_dir = s_web_directory}; mg_http_serve_dir(c, hm, &opts); } 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; 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); n = strlen("broadcast"); _ws_send(&g_mgr, (char *)"broadcast", n); } (void) fn_data; } void show_version(FILE *ofp) { fprintf(ofp, "piumsgd version %s\n", PIUMSGD_VERSION); } void show_help(FILE *ofp) { show_version(ofp); fprintf(ofp, "\npiusmgd usage:\n\n"); fprintf(ofp, " piumsgd [-h] [-v] [-p port] [-H host] [-D webdir] [-V]\n"); fprintf(ofp, "\n"); fprintf(ofp, " -D web directory (default %s)\n", PIUMSGD_DEFAULT_WEBDIR); fprintf(ofp, " -H host (default %s)\n", PIUMSGD_DEFAULT_ADDR); fprintf(ofp, " -p port (default %i)\n", PIUMSGD_DEFAULT_PORT); fprintf(ofp, " -V verbose\n"); fprintf(ofp, " -v show version\n"); fprintf(ofp, " -h help (this screen)\n"); fprintf(ofp, "\n"); } int main(int argc, char **argv) { int ch, opt_index; int nfd=0; int poll_return; int read_return; time_t now; int port = -1; char *host = NULL, *webdir = NULL; struct mg_conneciton *nc; char *listen_host = NULL; while ((ch = getopt_long(argc, argv, "hvVH:D:p:", long_opt, &opt_index)) >= 0) { switch (ch) { case 0: break; case 'h': show_help(stdout); exit(0); break; case 'v': show_version(stdout); exit(0); break; case 'V': g_verbose++; break; case 'p': port = atoi(optarg); break; case 'H': host = strdup(optarg); break; case 'D': s_web_directory = strdup(optarg); break; default: fprintf(stderr, "unknown option %c\n", ch); show_help(stderr); exit(-1); break; } } if(!s_web_directory) { s_web_directory = strdup(PIUMSGD_DEFAULT_WEBDIR); } if (port < 0) { port = PIUMSGD_DEFAULT_PORT; } if (!host) { host = strdup(PIUMSGD_DEFAULT_ADDR); } listen_host = (char *)malloc(sizeof(char)*2*strlen(host)); sprintf(listen_host, "http://%s:%i", host, port); // setup mongoose web server // mg_mgr_init(&g_mgr); //mg_http_listen(&g_mgr, "http://127.0.0.1:8001", _cb, NULL); //mg_http_listen(&g_mgr, "http://0.0.0.0:8001", _cb, NULL); mg_http_listen(&g_mgr, listen_host, _cb, NULL); for (;;) { mg_mgr_poll(&g_mgr, 200); } mg_mgr_free(&g_mgr); return 0; }