From 2c5684482c14764cec4fb32b2ec07dd3f77fd4bf Mon Sep 17 00:00:00 2001 From: Roland Reichwein Date: Mon, 20 Jan 2025 21:56:53 +0100 Subject: Add click-fcgi (WIP) --- MainLoop.cpp | 24 ++++ MainLoop.h | 7 ++ Makefile | 26 ++++- click-fcgi.cpp | 129 +++++++++++++++++++++ debian/control | 4 +- debian/nginx-sites-available | 22 ++++ html/favicon.ico | Bin 0 -> 2238 bytes html/index.html | 263 +++++++++++++++++++++++++++++++++++++++++++ 8 files changed, 470 insertions(+), 5 deletions(-) create mode 100644 click-fcgi.cpp create mode 100644 debian/nginx-sites-available create mode 100644 html/favicon.ico create mode 100644 html/index.html diff --git a/MainLoop.cpp b/MainLoop.cpp index b6331a2..ba026f2 100644 --- a/MainLoop.cpp +++ b/MainLoop.cpp @@ -22,6 +22,8 @@ using namespace std::chrono_literals; using namespace std::string_literals; +namespace bp = boost::process; + MainLoop::MainLoop(int argc, char** argv): m_status{}, m_config{argc, argv}, @@ -34,6 +36,28 @@ MainLoop::MainLoop(int argc, char** argv): { m_status.add(LED{"/sys/class/leds/ACT", "/sys/class/leds/PWR"}); m_status.add(LED{"/sys/class/leds/thingm1:green:led1", "/sys/class/leds/thingm1:red:led1"}); + + start_fcgi(); +} + +MainLoop::~MainLoop() +{ + stop_fcgi(); +} + +void MainLoop::start_fcgi() +{ + m_child_fcgi = bp::child("spawn-fcgi -a 127.0.0.1 -p 9090 -n -- ./click-fcgi"); + if (!m_child_fcgi.valid() || !m_child_fcgi.running()) { + throw std::runtime_error("click-fcgi not started"); + } +} + +void MainLoop::stop_fcgi() +{ + if (m_child_fcgi.valid()) { + m_child_fcgi.terminate(); + } } bool run_flag = true; diff --git a/MainLoop.h b/MainLoop.h index b732c32..4d08f5e 100644 --- a/MainLoop.h +++ b/MainLoop.h @@ -10,16 +10,23 @@ #include "PIDFile.h" #include +#include class MainLoop { public: MainLoop(int argc, char** argv); + ~MainLoop(); int run(); private: + void start_fcgi(); + void stop_fcgi(); + void reconfigure_mode(); + boost::process::child m_child_fcgi; + boost::signals2::connection m_click_connection; StatusLED m_status; diff --git a/Makefile b/Makefile index 91ee0c8..6b1121e 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,7 @@ TARGET=click +FCGI_TARGET=click-fcgi -default: $(TARGET) +default: $(TARGET) $(FCGI_TARGET) SRCS= \ MainLoop.cpp \ @@ -26,6 +27,14 @@ HEADERS=$(SRCS:.cpp=.h) OBJS=$(SRCS:.cpp=.o) +FCGI_SRCS=\ + click-fcgi.cpp \ + config.cpp \ + log.cpp \ + debug.cpp \ + +FCGI_OBJS=$(FCGI_SRCS:.cpp=.o) + CXX=clang++ ifeq ($(CXXFLAGS),) @@ -37,22 +46,33 @@ CXXFLAGS+=-std=c++20 -Wall -Wpedantic CXXFLAGS+=-gdwarf-4 CXXFLAGS+=-I/usr/include/libevdev-1.0 -CXXLIBS=$(shell pkg-config --libs alsa) -lreichwein -lfmt -lasound -levdev +CXXLIBS=$(shell pkg-config --libs alsa) -lreichwein -lfmt -lasound -levdev -lfcgi $(TARGET): $(OBJS) $(CXX) $^ -o $@ $(CXXLIBS) +$(FCGI_TARGET): $(FCGI_OBJS) + $(CXX) $^ -o $@ $(CXXLIBS) + %.o: %.cpp $(CXX) $(CXXFLAGS) -o $@ -c $< +run-fcgi: + spawn-fcgi -a 127.0.0.1 -p 9090 -n -- ./click-fcgi + install: mkdir -p $(DESTDIR)/usr/bin cp $(TARGET) $(DESTDIR)/usr/bin mkdir -p $(DESTDIR)/usr/lib/click/media cp media/click.s16le $(DESTDIR)/usr/lib/click/media + mkdir -p $(DESTDIR)/usr/lib/click + cp $(FCGI_TARGET) $(DESTDIR)/usr/lib/click + cp -r html $(DESTDIR)/usr/lib/click + mkdir -p $(DESTDIR)/etc/nginx/sites-available + cp debian/nginx-sites-available $(DESTDIR)/etc/nginx/sites-available/click clean: - rm -f $(TARGET) $(OBJS) + rm -f $(TARGET) $(FCGI_TARGET) $(OBJS) $(FCGI_OBJS) sound: ffmpeg -i media/click.wav -f s16le media/click.s16le diff --git a/click-fcgi.cpp b/click-fcgi.cpp new file mode 100644 index 0000000..8b422ca --- /dev/null +++ b/click-fcgi.cpp @@ -0,0 +1,129 @@ +#include "config.h" + +#include +#include +#include + +#include + +#include +#include +#include + +namespace pt = boost::property_tree; + +using namespace std::string_literals; + +namespace { + +class PostData +{ +public: + PostData(FCGX_Request& request) { + std::string result; + std::string contentLengthString(FCGX_GetParam("CONTENT_LENGTH", request.envp)); + int contentLength = std::stoul(contentLengthString); + + result.resize(contentLength); + + unsigned int status = FCGX_GetStr(result.data(), result.size(), request.in); + if (status != result.size()) { + throw std::runtime_error(fmt::format("Read error: {}/{}", status, result.size())); + } + + m_data = result; + } + + std::string getData() + { + return m_data; + } + + // path: xml path, e.g. data.value + std::string getXMLElement(const std::string& path) + { + pt::ptree tree{}; + std::istringstream iss{m_data}; + pt::read_xml(iss, tree, pt::xml_parser::trim_whitespace); + + return tree.get(path); + } + +private: + std::string m_data; +}; + +std::string getCommand(FCGX_Request& request) +{ + std::string query = FCGX_GetParam("QUERY_STRING", request.envp); + size_t pos = query.find("command="); + if (pos != query.npos) { + return query.substr(pos + 8); + } else { + return {}; + } +} + +std::string to_xml() +{ + std::string result{"ok"}; + + result += "ui1"; + return result + ""; +} + +} // namespace + +int main(int argc, char* argv[]) { + try { + Config config{argc, argv}; + + std::string ok_data{"okOK"}; + std::string error_data{"errorGeneral Error"}; + + int result = FCGX_Init(); + if (result != 0) { + return 1; // error on init + } + + FCGX_Request request; + + if (FCGX_InitRequest(&request, 0, 0) != 0) { + return 1; // error on init + } + + while (FCGX_Accept_r(&request) == 0) { + std::string method = FCGX_GetParam("REQUEST_METHOD", request.envp); + + FCGX_PutS("Content-Type: text/xml\r\n\r\n", request.out); + + try { + if (method == "POST") { + PostData data{request}; + std::string command {getCommand(request)}; + if (command == "start") { + FCGX_PutS(ok_data.c_str(), request.out); + } else if (command == "stop") { + FCGX_PutS(ok_data.c_str(), request.out); + } else if (command == "getui") { + FCGX_PutS(to_xml().c_str(), request.out); + } else if (command == "setfile") { + std::string filename = data.getXMLElement("data.value"); + FCGX_PutS(ok_data.c_str(), request.out); + } else { + FCGX_PutS(error_data.c_str(), request.out); + } + } else { + throw std::runtime_error(fmt::format("Bad request method: POST expected, got {}", method).c_str()); + } + } catch (const std::exception& ex) { + FCGX_PutS(("errorError: "s + ex.what() + "").c_str(), request.out); + } + } + } catch (const std::exception& ex) { + std::cerr << "Error: " << ex.what() << std::endl; + } + + return 0; +} + diff --git a/debian/control b/debian/control index de141e8..b95f2c8 100644 --- a/debian/control +++ b/debian/control @@ -2,13 +2,13 @@ Source: click Section: sound Priority: optional Maintainer: Roland Reichwein -Build-Depends: debhelper, clang, libc++-dev, libreichwein-dev, libasound2-dev, libfmt-dev, libboost-all-dev, libevdev-dev +Build-Depends: debhelper, clang, libc++-dev, libreichwein-dev, libasound2-dev, libfmt-dev, libboost-all-dev, libevdev-dev, libfcgi-dev Standards-Version: 4.5.0 Homepage: http://www.reichwein.it/click/ Package: click Architecture: any -Depends: ${shlibs:Depends}, ${misc:Depends} +Depends: ${shlibs:Depends}, ${misc:Depends}, spawn-fcgi, nginx, alsa-utils Homepage: http://www.reichwein.it/click/ Description: Software system for MIDI Click MIDI Click is a combined hardware-software solution to generate an audio diff --git a/debian/nginx-sites-available b/debian/nginx-sites-available new file mode 100644 index 0000000..f1ca5a1 --- /dev/null +++ b/debian/nginx-sites-available @@ -0,0 +1,22 @@ +server { + listen 80 default_server; + listen [::]:80 default_server; + + root /usr/lib/clock/html; + + # Add index.php to the list if you are using PHP + index index.html index.htm index.nginx-debian.html; + + server_name _; + + location ~ \.fcgi { + include fastcgi_params; + fastcgi_pass 127.0.0.1:9090; + } + + location / { + # First attempt to serve request as file, then + # as directory, then fall back to displaying a 404. + try_files $uri $uri/ =404; + } +} diff --git a/html/favicon.ico b/html/favicon.ico new file mode 100644 index 0000000..e8cbddb Binary files /dev/null and b/html/favicon.ico differ diff --git a/html/index.html b/html/index.html new file mode 100644 index 0000000..2fd9898 --- /dev/null +++ b/html/index.html @@ -0,0 +1,263 @@ + + + + CLICK + + + + + +
CLICK
+ +
+
+(Loading UI...) +
+
+
+ + + + + +
+
+
+ Status: (unknown) +
+ + + -- cgit v1.2.3