mirror of
https://github.com/zhaojh329/rtty.git
synced 2026-02-27 09:53:17 +08:00
replace C API by Lua
Signed-off-by: zhaojh329 <jianhuizhao329@gmail.com>
This commit is contained in:
@@ -1,15 +0,0 @@
|
||||
cmake_minimum_required (VERSION 2.6)
|
||||
|
||||
project(xterminal C)
|
||||
|
||||
add_definitions(-O -Wall -Werror --std=gnu99 -g3)
|
||||
|
||||
add_executable(xterminal_device xterminal_device.c)
|
||||
add_executable(xterminal_broker xterminal_broker.c)
|
||||
|
||||
target_link_libraries(xterminal_broker ev evmongoose)
|
||||
target_link_libraries(xterminal_device ev util evmongoose)
|
||||
|
||||
install(TARGETS xterminal_device xterminal_broker
|
||||
RUNTIME DESTINATION bin
|
||||
)
|
||||
@@ -1,9 +0,0 @@
|
||||
#ifndef __COMMON_H_
|
||||
#define __COMMON_H_
|
||||
|
||||
#include "list.h"
|
||||
|
||||
int asprintf(char **strp, const char *fmt, ...);
|
||||
void *memmem(const void *haystack, size_t haystacklen, const void *needle, size_t needlelen);
|
||||
|
||||
#endif
|
||||
+5
File diff suppressed because one or more lines are too long
@@ -1,208 +0,0 @@
|
||||
/*-
|
||||
* Copyright (c) 2011 Felix Fietkau <nbd@openwrt.org>
|
||||
* Copyright (c) 2010 Isilon Systems, Inc.
|
||||
* Copyright (c) 2010 iX Systems, Inc.
|
||||
* Copyright (c) 2010 Panasas, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice unmodified, this list of conditions, and the following
|
||||
* disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#ifndef _LINUX_LIST_H_
|
||||
#define _LINUX_LIST_H_
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#define prefetch(x)
|
||||
|
||||
#ifndef container_of
|
||||
#define container_of(ptr, type, member) \
|
||||
({ \
|
||||
const typeof(((type *) NULL)->member) *__mptr = (ptr); \
|
||||
(type *) ((char *) __mptr - offsetof(type, member)); \
|
||||
})
|
||||
#endif
|
||||
|
||||
struct list_head {
|
||||
struct list_head *next;
|
||||
struct list_head *prev;
|
||||
};
|
||||
|
||||
#define LIST_HEAD_INIT(name) { &(name), &(name) }
|
||||
#undef LIST_HEAD
|
||||
#define LIST_HEAD(name) struct list_head name = LIST_HEAD_INIT(name)
|
||||
|
||||
static inline void
|
||||
INIT_LIST_HEAD(struct list_head *list)
|
||||
{
|
||||
list->next = list->prev = list;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
list_empty(const struct list_head *head)
|
||||
{
|
||||
return (head->next == head);
|
||||
}
|
||||
|
||||
static inline bool
|
||||
list_is_first(const struct list_head *list,
|
||||
const struct list_head *head)
|
||||
{
|
||||
return list->prev == head;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
list_is_last(const struct list_head *list,
|
||||
const struct list_head *head)
|
||||
{
|
||||
return list->next == head;
|
||||
}
|
||||
|
||||
static inline void
|
||||
_list_del(struct list_head *entry)
|
||||
{
|
||||
entry->next->prev = entry->prev;
|
||||
entry->prev->next = entry->next;
|
||||
}
|
||||
|
||||
static inline void
|
||||
list_del(struct list_head *entry)
|
||||
{
|
||||
_list_del(entry);
|
||||
entry->next = entry->prev = NULL;
|
||||
}
|
||||
|
||||
static inline void
|
||||
_list_add(struct list_head *_new, struct list_head *prev,
|
||||
struct list_head *next)
|
||||
{
|
||||
|
||||
next->prev = _new;
|
||||
_new->next = next;
|
||||
_new->prev = prev;
|
||||
prev->next = _new;
|
||||
}
|
||||
|
||||
static inline void
|
||||
list_del_init(struct list_head *entry)
|
||||
{
|
||||
_list_del(entry);
|
||||
INIT_LIST_HEAD(entry);
|
||||
}
|
||||
|
||||
#define list_entry(ptr, type, field) container_of(ptr, type, field)
|
||||
#define list_first_entry(ptr, type, field) list_entry((ptr)->next, type, field)
|
||||
#define list_last_entry(ptr, type, field) list_entry((ptr)->prev, type, field)
|
||||
|
||||
#define list_for_each(p, head) \
|
||||
for (p = (head)->next; p != (head); p = p->next)
|
||||
|
||||
#define list_for_each_safe(p, n, head) \
|
||||
for (p = (head)->next, n = p->next; p != (head); p = n, n = p->next)
|
||||
|
||||
#define list_for_each_entry(p, h, field) \
|
||||
for (p = list_first_entry(h, typeof(*p), field); &p->field != (h); \
|
||||
p = list_entry(p->field.next, typeof(*p), field))
|
||||
|
||||
#define list_for_each_entry_safe(p, n, h, field) \
|
||||
for (p = list_first_entry(h, typeof(*p), field), \
|
||||
n = list_entry(p->field.next, typeof(*p), field); &p->field != (h);\
|
||||
p = n, n = list_entry(n->field.next, typeof(*n), field))
|
||||
|
||||
#define list_for_each_entry_reverse(p, h, field) \
|
||||
for (p = list_last_entry(h, typeof(*p), field); &p->field != (h); \
|
||||
p = list_entry(p->field.prev, typeof(*p), field))
|
||||
|
||||
#define list_for_each_prev(p, h) for (p = (h)->prev; p != (h); p = p->prev)
|
||||
#define list_for_each_prev_safe(p, n, h) for (p = (h)->prev, n = p->prev; p != (h); p = n, n = p->prev)
|
||||
|
||||
static inline void
|
||||
list_add(struct list_head *_new, struct list_head *head)
|
||||
{
|
||||
_list_add(_new, head, head->next);
|
||||
}
|
||||
|
||||
static inline void
|
||||
list_add_tail(struct list_head *_new, struct list_head *head)
|
||||
{
|
||||
_list_add(_new, head->prev, head);
|
||||
}
|
||||
|
||||
static inline void
|
||||
list_move(struct list_head *list, struct list_head *head)
|
||||
{
|
||||
_list_del(list);
|
||||
list_add(list, head);
|
||||
}
|
||||
|
||||
static inline void
|
||||
list_move_tail(struct list_head *entry, struct list_head *head)
|
||||
{
|
||||
_list_del(entry);
|
||||
list_add_tail(entry, head);
|
||||
}
|
||||
|
||||
static inline void
|
||||
_list_splice(const struct list_head *list, struct list_head *prev,
|
||||
struct list_head *next)
|
||||
{
|
||||
struct list_head *first;
|
||||
struct list_head *last;
|
||||
|
||||
if (list_empty(list))
|
||||
return;
|
||||
|
||||
first = list->next;
|
||||
last = list->prev;
|
||||
first->prev = prev;
|
||||
prev->next = first;
|
||||
last->next = next;
|
||||
next->prev = last;
|
||||
}
|
||||
|
||||
static inline void
|
||||
list_splice(const struct list_head *list, struct list_head *head)
|
||||
{
|
||||
_list_splice(list, head, head->next);
|
||||
}
|
||||
|
||||
static inline void
|
||||
list_splice_tail(struct list_head *list, struct list_head *head)
|
||||
{
|
||||
_list_splice(list, head->prev, head);
|
||||
}
|
||||
|
||||
static inline void
|
||||
list_splice_init(struct list_head *list, struct list_head *head)
|
||||
{
|
||||
_list_splice(list, head, head->next);
|
||||
INIT_LIST_HEAD(list);
|
||||
}
|
||||
|
||||
static inline void
|
||||
list_splice_tail_init(struct list_head *list, struct list_head *head)
|
||||
{
|
||||
_list_splice(list, head->prev, head);
|
||||
INIT_LIST_HEAD(list);
|
||||
}
|
||||
|
||||
#endif /* _LINUX_LIST_H_ */
|
||||
@@ -1,27 +1,32 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||
<title>Websocket Test</title>
|
||||
</head>
|
||||
<body>
|
||||
<button type="button" onclick="connect()">Connect</button>
|
||||
<script>
|
||||
function connect() {
|
||||
var ws = new WebSocket("ws://" + location.host);
|
||||
ws.onopen = function() {
|
||||
console.log("connect ok");
|
||||
ws.send("Hello WebSocket");
|
||||
};
|
||||
|
||||
ws.onmessage = function (evt) {
|
||||
console.log("recv:"+evt.data);
|
||||
};
|
||||
|
||||
ws.onclose = function() {
|
||||
console.log("close");
|
||||
};
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<link rel="stylesheet" href="xterm.css" />
|
||||
<script src="jquery-1.12.4.min.js"></script>
|
||||
<script src="xterm.js"></script>
|
||||
<script src="utf8.js"></script>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<div id="terminal"></div>
|
||||
<script>
|
||||
|
||||
var ws = new WebSocket("ws://" + location.host);
|
||||
var term = new Terminal();
|
||||
ws.onopen = function() {
|
||||
term.open(document.getElementById('terminal'));
|
||||
|
||||
ws.send("connect 448A5BEC4927");
|
||||
|
||||
term.on('data', function(data) {
|
||||
ws.send(data);
|
||||
});
|
||||
};
|
||||
|
||||
ws.onmessage = function (e) {
|
||||
term.write(e.data);
|
||||
};
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,254 +0,0 @@
|
||||
#include <mongoose.h>
|
||||
#include <termios.h>
|
||||
#include "common.h"
|
||||
|
||||
static LIST_HEAD(device_list);
|
||||
static LIST_HEAD(session_list);
|
||||
|
||||
struct device {
|
||||
char mac[13];
|
||||
int alive;
|
||||
struct list_head node;
|
||||
};
|
||||
|
||||
struct session {
|
||||
char id[33];
|
||||
struct device *dev;
|
||||
struct list_head node;
|
||||
struct mg_connection *nc;
|
||||
};
|
||||
|
||||
static struct session *find_session_by_nc(struct mg_connection *nc)
|
||||
{
|
||||
struct session *s = NULL;
|
||||
|
||||
list_for_each_entry(s, &session_list, node) {
|
||||
if (s->nc == nc)
|
||||
return s;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct device *find_device_by_mac(struct mg_str *mac)
|
||||
{
|
||||
struct device *dev = NULL;
|
||||
|
||||
list_for_each_entry(dev, &device_list, node) {
|
||||
if (!mg_vcmp(mac, dev->mac))
|
||||
return dev;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void update_device(struct mg_str *mac)
|
||||
{
|
||||
struct device *dev = find_device_by_mac(mac);
|
||||
if (!dev) {
|
||||
dev = calloc(1, sizeof(struct device));
|
||||
if (!dev) {
|
||||
perror("calloc");
|
||||
return;
|
||||
}
|
||||
|
||||
list_add(&dev->node, &device_list);
|
||||
strncpy(dev->mac, mac->p, mac->len);
|
||||
}
|
||||
|
||||
dev->alive = 10;
|
||||
}
|
||||
|
||||
static struct session *session_new(struct mg_connection *nc, struct mg_str *mac)
|
||||
{
|
||||
char buf[128] = "";
|
||||
struct timeval tv;
|
||||
struct session *s = NULL;
|
||||
struct device *dev = NULL;
|
||||
|
||||
dev = find_device_by_mac(mac);
|
||||
if (!dev)
|
||||
return NULL;
|
||||
|
||||
s = calloc(1, sizeof(struct session));
|
||||
if (!s) {
|
||||
perror("calloc");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
list_add(&s->node, &session_list);
|
||||
s->dev = dev;
|
||||
|
||||
gettimeofday(&tv, NULL);
|
||||
|
||||
snprintf(buf, sizeof(buf), "%ld", tv.tv_sec * 1000 + tv.tv_usec / 1000);
|
||||
|
||||
cs_md5(s->id, buf, strlen(buf), NULL);
|
||||
|
||||
s->nc = nc;
|
||||
printf("new session:%s\n", s->id);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
static struct mg_connection *mqtt_nc;
|
||||
static struct mg_connection *websocket_nc;
|
||||
|
||||
static void ev_handler(struct mg_connection *nc, int ev, void *ev_data)
|
||||
{
|
||||
if (ev == MG_EV_CONNECT) {
|
||||
/* Connect to mqtt broker */
|
||||
struct mg_send_mqtt_handshake_opts opts;
|
||||
memset(&opts, 0, sizeof(opts));
|
||||
|
||||
mg_set_protocol_mqtt(nc);
|
||||
mg_send_mqtt_handshake_opt(nc, "xxxx12342", opts);
|
||||
|
||||
} else if (ev == MG_EV_MQTT_CONNACK) {
|
||||
struct mg_mqtt_message *msg = (struct mg_mqtt_message *)ev_data;
|
||||
struct mg_mqtt_topic_expression topic_expr[1];
|
||||
|
||||
if (msg->connack_ret_code != MG_EV_MQTT_CONNACK_ACCEPTED) {
|
||||
printf("Got mqtt connection error: %d\n", msg->connack_ret_code);
|
||||
ev_break(EV_DEFAULT, EVBREAK_ALL);
|
||||
return;
|
||||
}
|
||||
|
||||
topic_expr[0].qos = MG_MQTT_QOS(0);
|
||||
asprintf((char **)&topic_expr[0].topic, "xterminal/heartbeat");
|
||||
|
||||
mg_mqtt_subscribe(nc, topic_expr, 1, 0);
|
||||
|
||||
free((char *)topic_expr[0].topic);
|
||||
|
||||
mqtt_nc = nc;
|
||||
|
||||
} else if (ev == MG_EV_MQTT_PUBLISH) {
|
||||
struct mg_mqtt_message *msg = (struct mg_mqtt_message *)ev_data;
|
||||
|
||||
if (!mg_vcmp(&msg->topic, "xterminal/heartbeat")) {
|
||||
update_device(&msg->payload);
|
||||
} else if (memmem(msg->topic.p, msg->topic.len, "devdata", strlen("devdata"))) {
|
||||
mg_send_websocket_frame(websocket_nc, WEBSOCKET_OP_TEXT, msg->payload.p, msg->payload.len);
|
||||
}
|
||||
|
||||
} else if (ev == MG_EV_HTTP_REQUEST) {
|
||||
struct http_message *hm = (struct http_message *)ev_data;
|
||||
if (!mg_vcmp(&hm->uri, "/list")) {
|
||||
struct device *dev;
|
||||
|
||||
mg_send_head(nc, 200, -1, NULL);
|
||||
|
||||
mg_printf_http_chunk(nc, "[");
|
||||
|
||||
list_for_each_entry(dev, &device_list, node) {
|
||||
mg_printf_http_chunk(nc, "\"%s\",", dev->mac);
|
||||
}
|
||||
|
||||
mg_printf_http_chunk(nc, "\"\"]");
|
||||
|
||||
mg_send_http_chunk(nc, "", 0);
|
||||
} else {
|
||||
struct mg_serve_http_opts opts = {};
|
||||
mg_serve_http(nc, hm, opts);
|
||||
}
|
||||
} else if (ev == MG_EV_WEBSOCKET_HANDSHAKE_DONE) {
|
||||
websocket_nc = nc;
|
||||
|
||||
} else if (ev == MG_EV_WEBSOCKET_FRAME) {
|
||||
struct websocket_message *wm = (struct websocket_message *)ev_data;
|
||||
|
||||
|
||||
if (wm->flags & WEBSOCKET_OP_TEXT) {
|
||||
const char *pattern = "connect ????????????";
|
||||
const struct mg_str pstr = {pattern, strlen(pattern)};
|
||||
|
||||
if (mg_match_prefix_n(pstr, mg_mk_str_n((const char *)wm->data, wm->size)) > 0) {
|
||||
char topic[128] = "";
|
||||
struct mg_mqtt_topic_expression topic_expr[1];
|
||||
struct mg_str mac = mg_mk_str_n((const char *)(wm->data + 8), wm->size - 8);
|
||||
struct session *s = session_new(nc, &mac);
|
||||
|
||||
if (!s)
|
||||
return;
|
||||
|
||||
topic_expr[0].qos = MG_MQTT_QOS(0);
|
||||
asprintf((char **)&topic_expr[0].topic, "xterminal/%.*s/%s/devdata", (int)mac.len, mac.p, s->id);
|
||||
mg_mqtt_subscribe(mqtt_nc, topic_expr, 1, 44);
|
||||
free((char *)topic_expr[0].topic);
|
||||
|
||||
sprintf(topic, "xterminal/%.*s/connect/%s", (int)mac.len, mac.p, s->id);
|
||||
mg_mqtt_publish(mqtt_nc, topic, 0, MG_MQTT_QOS(0), NULL, 0);
|
||||
} else {
|
||||
char topic[128] = "";
|
||||
struct session *s = find_session_by_nc(nc);
|
||||
sprintf(topic, "xterminal/%s/%s/srvdata", s->dev->mac, s->id);
|
||||
mg_mqtt_publish(mqtt_nc, topic, 0, MG_MQTT_QOS(0), wm->data, wm->size);
|
||||
printf("pub to:%s %.*s\n", topic, (int)wm->size, wm->data);
|
||||
}
|
||||
}
|
||||
} else if (ev == MG_EV_CLOSE) {
|
||||
//printf("connection close:%p\n", nc);
|
||||
}
|
||||
}
|
||||
|
||||
static void device_alive_cb(struct ev_loop *loop, ev_timer *w, int revents)
|
||||
{
|
||||
struct device *dev, *tmp;
|
||||
|
||||
list_for_each_entry_safe(dev, tmp, &device_list, node) {
|
||||
if (dev->alive-- == 0) {
|
||||
printf("del timeout device:%s\n", dev->mac);
|
||||
list_del(&dev->node);
|
||||
free(dev);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void signal_cb(struct ev_loop *loop, ev_signal *w, int revents)
|
||||
{
|
||||
printf("Got signal: %d\n", w->signum);
|
||||
ev_break(loop, EVBREAK_ALL);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
struct ev_loop *loop = EV_DEFAULT;
|
||||
ev_signal sig_watcher;
|
||||
struct mg_mgr mgr;
|
||||
struct mg_connection *nc;
|
||||
const char *s_address = "localhost:1883";
|
||||
const char *s_http_port = "8000";
|
||||
ev_timer device_timer;
|
||||
|
||||
ev_signal_init(&sig_watcher, signal_cb, SIGINT);
|
||||
ev_signal_start(loop, &sig_watcher);
|
||||
|
||||
ev_timer_init(&device_timer, device_alive_cb, 0.1, 2.0);
|
||||
ev_timer_start(loop, &device_timer);
|
||||
|
||||
mg_mgr_init(&mgr, NULL);
|
||||
|
||||
nc = mg_connect(&mgr, s_address, ev_handler);
|
||||
if (!nc) {
|
||||
fprintf(stderr, "mg_connect(%s) failed\n", s_address);
|
||||
}
|
||||
|
||||
nc = mg_bind(&mgr, s_http_port, ev_handler);
|
||||
if (nc == NULL) {
|
||||
printf("Failed to create listener\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
mg_set_protocol_http_websocket(nc);
|
||||
|
||||
ev_run(loop, 0);
|
||||
|
||||
printf("exit...\n");
|
||||
|
||||
mg_mgr_free(&mgr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
Executable
+114
@@ -0,0 +1,114 @@
|
||||
#!/usr/bin/lua
|
||||
|
||||
local ev = require("ev")
|
||||
local evmg = require("evmongoose")
|
||||
local posix = require 'posix'
|
||||
local cjson = require("cjson")
|
||||
|
||||
local loop = ev.Loop.default
|
||||
|
||||
local mgr = evmg.init()
|
||||
|
||||
local device = {}
|
||||
local session = {}
|
||||
|
||||
local function find_sid_by_websocket(nc)
|
||||
for k, v in pairs(session) do
|
||||
if v.websocket_nc == nc then
|
||||
return k
|
||||
end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
local function ev_handle(nc, event, msg)
|
||||
if event == evmg.MG_EV_CONNECT then
|
||||
mgr:set_protocol_mqtt(nc)
|
||||
mgr:send_mqtt_handshake_opt(nc)
|
||||
|
||||
elseif event == evmg.MG_EV_MQTT_CONNACK then
|
||||
if msg.connack_ret_code ~= evmg.MG_EV_MQTT_CONNACK_ACCEPTED then
|
||||
print("Got mqtt connection error:", msg.connack_ret_code)
|
||||
return
|
||||
end
|
||||
|
||||
mgr:mqtt_subscribe(nc, "xterminal/heartbeat");
|
||||
|
||||
ev.Timer.new(function(loop, timer, revents)
|
||||
for k, v in pairs(device) do
|
||||
v.alive = v.alive - 1
|
||||
if v.alive == 0 then
|
||||
device[k] = nil
|
||||
print("timeout:", k)
|
||||
end
|
||||
end
|
||||
end, 1, 3):start(loop)
|
||||
|
||||
elseif event == evmg.MG_EV_MQTT_PUBLISH then
|
||||
local topic = msg.topic
|
||||
print("mqtt recv:", topic)
|
||||
if topic == "xterminal/heartbeat" then
|
||||
local mac = msg.payload
|
||||
if not device[mac] then
|
||||
device[mac] = {}
|
||||
print("new dev:", mac)
|
||||
end
|
||||
device[mac] = {alive = 5, mqtt_nc = nc}
|
||||
elseif topic:match("devdata") then
|
||||
local mac, sid = topic:match("xterminal/(%w+)/(%w+)/devdata")
|
||||
mgr:send_websocket_frame(session[sid].websocket_nc, msg.payload)
|
||||
end
|
||||
|
||||
elseif event == evmg.MG_EV_HTTP_REQUEST then
|
||||
local uri = msg.uri
|
||||
if uri == "/list" then
|
||||
mgr:send_head(nc, 200, -1)
|
||||
|
||||
local devs = {}
|
||||
|
||||
for k, v in pairs(device) do
|
||||
devs[#devs + 1] = k
|
||||
end
|
||||
|
||||
mgr:print_http_chunk(nc, cjson.encode(devs))
|
||||
mgr:print_http_chunk(nc, "")
|
||||
return true
|
||||
end
|
||||
|
||||
elseif event == evmg.MG_EV_WEBSOCKET_FRAME then
|
||||
local data = msg.data
|
||||
if data:match("connect ") then
|
||||
local mac = data:match("connect (%w+)")
|
||||
local sid = "fsdg23423"
|
||||
|
||||
session[sid] = {
|
||||
websocket_nc = nc,
|
||||
mac = mac
|
||||
}
|
||||
|
||||
mgr:mqtt_subscribe(device[mac].mqtt_nc, "xterminal/" .. mac .. "/" .. sid .. "/devdata");
|
||||
mgr:mqtt_publish(device[mac].mqtt_nc, "xterminal/" .. mac .. "/connect/" .. sid, "");
|
||||
else
|
||||
local sid = find_sid_by_websocket(nc)
|
||||
local s = session[sid]
|
||||
mgr:mqtt_publish(device[s.mac].mqtt_nc, "xterminal/" .. s.mac .. "/" .. sid .. "/srvdata", msg.data);
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
mgr:connect("localhost:1883", ev_handle)
|
||||
|
||||
mgr:bind("8000", ev_handle, {proto = "http"})
|
||||
print("Listen on http 8000...")
|
||||
|
||||
ev.Signal.new(function(loop, sig, revents)
|
||||
loop:unloop()
|
||||
end, ev.SIGINT):start(loop)
|
||||
|
||||
print("start...")
|
||||
|
||||
loop:loop()
|
||||
|
||||
mgr:destroy()
|
||||
|
||||
print("exit...")
|
||||
@@ -1,225 +0,0 @@
|
||||
#include <mongoose.h>
|
||||
#include <pty.h>
|
||||
#include <net/if.h>
|
||||
#include "common.h"
|
||||
|
||||
struct session {
|
||||
pid_t pid;
|
||||
int pty;
|
||||
char id[33];
|
||||
struct list_head node;
|
||||
ev_io watcher;
|
||||
char topic_dev[128];
|
||||
char topic_srv[128];
|
||||
struct mg_connection *nc;
|
||||
};
|
||||
|
||||
static char *devid;
|
||||
static LIST_HEAD(session_list);
|
||||
|
||||
static void pty_read_cb(struct ev_loop *loop, ev_io *w, int revents)
|
||||
{
|
||||
int len = 0;
|
||||
char buf[1024] = "";
|
||||
struct session *s = (struct session *)w->data;
|
||||
|
||||
len = read(w->fd, buf, sizeof(buf));
|
||||
if (len > 0) {
|
||||
mg_mqtt_publish(s->nc, s->topic_dev, 0, MG_MQTT_QOS(0), buf, len);
|
||||
printf("pub to:%s %s\n", s->topic_dev, buf);
|
||||
} else {
|
||||
perror("read");
|
||||
ev_io_stop(loop, w);
|
||||
}
|
||||
}
|
||||
|
||||
static struct session *find_session_by_id(char *id)
|
||||
{
|
||||
struct session *s = NULL;
|
||||
|
||||
list_for_each_entry(s, &session_list, node) {
|
||||
if (!strcmp(s->id, id))
|
||||
return s;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void new_connect(struct mg_connection *nc, char *id)
|
||||
{
|
||||
struct session *s = NULL;
|
||||
pid_t pid;
|
||||
int pty;
|
||||
struct mg_mqtt_topic_expression topic_expr[1];
|
||||
|
||||
pid = forkpty(&pty, NULL, NULL, NULL);
|
||||
if (pid < 0) {
|
||||
perror("forkpty");
|
||||
return;
|
||||
} else if (pid == 0) {
|
||||
execl("/bin/login", "login", NULL);
|
||||
}
|
||||
|
||||
s = calloc(1, sizeof(struct session));
|
||||
if (!s) {
|
||||
perror("calloc");
|
||||
return;
|
||||
}
|
||||
|
||||
list_add(&s->node, &session_list);
|
||||
strncpy(s->id, id, sizeof(s->id));
|
||||
s->pid = pid;
|
||||
s->pty = pty;
|
||||
s->nc = nc;
|
||||
|
||||
topic_expr[0].qos = MG_MQTT_QOS(0);
|
||||
asprintf((char **)&topic_expr[0].topic, "xterminal/%s/%s/srvdata", devid, id);
|
||||
mg_mqtt_subscribe(nc, topic_expr, 1, 44);
|
||||
printf("sub:%s\n", topic_expr[0].topic);
|
||||
free((char *)topic_expr[0].topic);
|
||||
|
||||
snprintf(s->topic_dev, sizeof(s->topic_dev), "xterminal/%s/%s/devdata", devid, id);
|
||||
|
||||
ev_io_init(&s->watcher, pty_read_cb, pty, EV_READ);
|
||||
s->watcher.data = s;
|
||||
ev_io_start(EV_DEFAULT, &s->watcher);
|
||||
}
|
||||
|
||||
static void ev_handler(struct mg_connection *nc, int ev, void *ev_data)
|
||||
{
|
||||
if (ev == MG_EV_CONNECT) {
|
||||
/* Connect to mqtt broker */
|
||||
struct mg_send_mqtt_handshake_opts opts;
|
||||
memset(&opts, 0, sizeof(opts));
|
||||
|
||||
mg_set_protocol_mqtt(nc);
|
||||
mg_send_mqtt_handshake_opt(nc, devid, opts);
|
||||
|
||||
} else if (ev == MG_EV_MQTT_CONNACK) {
|
||||
struct mg_mqtt_message *msg = (struct mg_mqtt_message *)ev_data;
|
||||
struct mg_mqtt_topic_expression topic_expr[1];
|
||||
|
||||
if (msg->connack_ret_code != MG_EV_MQTT_CONNACK_ACCEPTED) {
|
||||
printf("Got mqtt connection error: %d\n", msg->connack_ret_code);
|
||||
ev_break(EV_DEFAULT, EVBREAK_ALL);
|
||||
return;
|
||||
}
|
||||
|
||||
topic_expr[0].qos = MG_MQTT_QOS(0);
|
||||
asprintf((char **)&topic_expr[0].topic, "xterminal/%s/connect/+", devid);
|
||||
|
||||
mg_mqtt_subscribe(nc, topic_expr, 1, 0);
|
||||
|
||||
free((char *)topic_expr[0].topic);
|
||||
|
||||
} else if (ev == MG_EV_MQTT_PUBLISH) {
|
||||
struct mg_mqtt_message *msg = (struct mg_mqtt_message *)ev_data;
|
||||
|
||||
if (memmem(msg->topic.p, msg->topic.len, "connect", strlen("connect"))) {
|
||||
char id[33] = "";
|
||||
strncpy(id, msg->topic.p + 31, 32);
|
||||
|
||||
new_connect(nc, id);
|
||||
} else {
|
||||
char id[33] = "";
|
||||
struct session *s = NULL;
|
||||
|
||||
strncpy(id, msg->topic.p + 23, 32);
|
||||
s = find_session_by_id(id);
|
||||
if (s) {
|
||||
int ret = write(s->pty, msg->payload.p, msg->payload.len);
|
||||
if (ret < 0)
|
||||
perror("write");
|
||||
}
|
||||
|
||||
}
|
||||
} else if (ev == MG_EV_CLOSE) {
|
||||
printf("connection close:%p\n", nc);
|
||||
}
|
||||
}
|
||||
|
||||
static void signal_cb(struct ev_loop *loop, ev_signal *w, int revents)
|
||||
{
|
||||
if (w->signum == SIGINT) {
|
||||
ev_break(loop, EVBREAK_ALL);
|
||||
}
|
||||
}
|
||||
|
||||
static void heartbeat_cb(struct ev_loop *loop, ev_timer *w, int revents)
|
||||
{
|
||||
struct mg_connection *nc = (struct mg_connection *)w->data;
|
||||
mg_mqtt_publish(nc, "xterminal/heartbeat", 0, MG_MQTT_QOS(0), devid, strlen(devid));
|
||||
}
|
||||
|
||||
static int get_iface_mac(const char *ifname, char mac[6])
|
||||
{
|
||||
int r, s;
|
||||
struct ifreq ifr;
|
||||
|
||||
strncpy(ifr.ifr_name, ifname, 15);
|
||||
ifr.ifr_name[15] = '\0';
|
||||
|
||||
s = socket(AF_INET, SOCK_DGRAM, 0);
|
||||
if (-1 == s) {
|
||||
perror("socket");
|
||||
return -1;
|
||||
}
|
||||
|
||||
r = ioctl(s, SIOCGIFHWADDR, &ifr);
|
||||
if (r == -1) {
|
||||
perror("ioctl");
|
||||
close(s);
|
||||
return -1;
|
||||
}
|
||||
|
||||
close(s);
|
||||
|
||||
memcpy(mac, ifr.ifr_hwaddr.sa_data, 6);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
struct ev_loop *loop = EV_DEFAULT;
|
||||
ev_signal sigint_watcher;
|
||||
ev_timer heartbeat_timer;
|
||||
struct mg_mgr mgr;
|
||||
struct mg_connection *nc;
|
||||
char mac[6] = "";
|
||||
const char *s_address = "localhost:1883";
|
||||
|
||||
mg_mgr_init(&mgr, NULL);
|
||||
|
||||
nc = mg_connect(&mgr, s_address, ev_handler);
|
||||
if (!nc) {
|
||||
fprintf(stderr, "mg_connect(%s) failed\n", s_address);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
ev_signal_init(&sigint_watcher, signal_cb, SIGINT);
|
||||
sigint_watcher.data = nc;
|
||||
ev_signal_start(loop, &sigint_watcher);
|
||||
|
||||
ev_timer_init(&heartbeat_timer, heartbeat_cb, 0.1, 2.0);
|
||||
heartbeat_timer.data = nc;
|
||||
ev_timer_start(loop, &heartbeat_timer);
|
||||
|
||||
get_iface_mac("enp3s0", mac);
|
||||
|
||||
devid = calloc(1, 13);
|
||||
sprintf(devid, "%02hhX%02hhX%02hhX%02hhX%02hhX%02hhX",
|
||||
mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
|
||||
|
||||
printf("devid:%s\n", devid);
|
||||
|
||||
ev_run(loop, 0);
|
||||
|
||||
printf("exit...\n");
|
||||
|
||||
free(devid);
|
||||
|
||||
mg_mgr_free(&mgr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
Executable
+83
@@ -0,0 +1,83 @@
|
||||
#!/usr/bin/lua
|
||||
|
||||
local ev = require("ev")
|
||||
local evmg = require("evmongoose")
|
||||
local posix = require 'posix'
|
||||
local cjson = require("cjson")
|
||||
|
||||
local loop = ev.Loop.default
|
||||
|
||||
local mgr = evmg.init()
|
||||
local devid = "448A5BEC4927"
|
||||
local session = {}
|
||||
|
||||
local function new_connect(nc, id)
|
||||
local pid, pty = evmg.forkpty()
|
||||
if pid == 0 then
|
||||
posix.exec ("/bin/login")
|
||||
end
|
||||
|
||||
session[id] = {
|
||||
pid = pid,
|
||||
pty = pty
|
||||
}
|
||||
|
||||
mgr:mqtt_subscribe(nc, "xterminal/" .. devid .. "/" .. id .. "/srvdata");
|
||||
|
||||
local topic_dev = "xterminal/" .. devid .. "/" .. id .. "/devdata"
|
||||
|
||||
ev.IO.new(function(loop, w, revents)
|
||||
local d, err = posix.read(w:getfd(), 1024)
|
||||
if not d then
|
||||
w:stop(loop)
|
||||
error("read error:" .. err)
|
||||
end
|
||||
mgr:mqtt_publish(nc, topic_dev, d);
|
||||
end, pty, ev.READ):start(loop)
|
||||
|
||||
print("new connect ok")
|
||||
end
|
||||
|
||||
local function ev_handle(nc, event, msg)
|
||||
if event == evmg.MG_EV_CONNECT then
|
||||
mgr:set_protocol_mqtt(nc)
|
||||
mgr:send_mqtt_handshake_opt(nc)
|
||||
|
||||
elseif event == evmg.MG_EV_MQTT_CONNACK then
|
||||
if msg.connack_ret_code ~= evmg.MG_EV_MQTT_CONNACK_ACCEPTED then
|
||||
print("Got mqtt connection error:", msg.connack_ret_code)
|
||||
return
|
||||
end
|
||||
|
||||
mgr:mqtt_subscribe(nc, "xterminal/" .. devid .. "/connect/+");
|
||||
|
||||
ev.Timer.new(function(loop, timer, revents)
|
||||
mgr:mqtt_publish(nc, "xterminal/heartbeat", devid)
|
||||
end, 0.1, 3):start(loop)
|
||||
|
||||
elseif event == evmg.MG_EV_MQTT_PUBLISH then
|
||||
local topic = msg.topic
|
||||
print("mqtt recv:", topic)
|
||||
if topic:match("connect") then
|
||||
local id = topic:match("xterminal/" .. devid .. "/connect/(%w+)")
|
||||
new_connect(nc, id)
|
||||
else
|
||||
local id = topic:match("xterminal/" .. devid .. "/(%w+)/srvdata")
|
||||
posix.write(session[id].pty, msg.payload);
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
mgr:connect("localhost:1883", ev_handle)
|
||||
|
||||
ev.Signal.new(function(loop, sig, revents)
|
||||
loop:unloop()
|
||||
end, ev.SIGINT):start(loop)
|
||||
|
||||
print("start...")
|
||||
|
||||
loop:loop()
|
||||
|
||||
mgr:destroy()
|
||||
|
||||
print("exit...")
|
||||
Reference in New Issue
Block a user