/*
* 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;
}