From 7bfcf9af5c7932814f4997aa8ac51270e8cfc985 Mon Sep 17 00:00:00 2001 From: falkTX Date: Tue, 27 Aug 2013 09:16:07 +0100 Subject: [PATCH] Actually add file --- source/utils/CarlaPipeUtils.hpp | 498 ++++++++++++++++++++++++++++++++ 1 file changed, 498 insertions(+) create mode 100644 source/utils/CarlaPipeUtils.hpp diff --git a/source/utils/CarlaPipeUtils.hpp b/source/utils/CarlaPipeUtils.hpp new file mode 100644 index 000000000..7aa584119 --- /dev/null +++ b/source/utils/CarlaPipeUtils.hpp @@ -0,0 +1,498 @@ +/* + * Carla Pipe utils + * Copyright (C) 2013 Filipe Coelho + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or any later version. + * + * This program 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 General Public License for more details. + * + * For a full copy of the GNU General Public License see the doc/GPL.txt file. + */ + +#ifndef CARLA_PIPE_UTILS_HPP_INCLUDED +#define CARLA_PIPE_UTILS_HPP_INCLUDED + +#define WAIT_START_TIMEOUT 3000 /* ms */ +#define WAIT_ZOMBIE_TIMEOUT 3000 /* ms */ +#define WAIT_STEP 100 /* ms */ + +#include "CarlaUtils.hpp" +#include "CarlaString.hpp" + +#include +#include + +// ----------------------------------------------------------------------- + +class CarlaPipeServer +{ +protected: + CarlaPipeServer() + : fPipeRecv(-1), + fPipeSend(-1), + fPid(-1), + fReading(false) + { + carla_debug("CarlaPipeServer::CarlaPipeServer()"); + } + + // ------------------------------------------------------------------- + +public: + virtual ~CarlaPipeServer() + { + carla_debug("CarlaPipeServer::~CarlaPipeServer()"); + + stop(); + } + + void start(const char* const filename, const char* const arg1, const char* const arg2) + { + CARLA_SAFE_ASSERT_RETURN(filename != nullptr,) + CARLA_SAFE_ASSERT_RETURN(arg1 != nullptr,) + CARLA_SAFE_ASSERT_RETURN(arg2 != nullptr,) + carla_debug("CarlaPipeServer::start(\"%s\", \"%s\", \"%s\"", filename, arg1, arg2); + + //---------------------------------------------------------------- + + const char* argv[6]; + + //---------------------------------------------------------------- + // argv[0] => filename + + argv[0] = filename; + + //---------------------------------------------------------------- + // argv[1-2] => args + + argv[1] = arg1; + argv[2] = arg2; + + //---------------------------------------------------------------- + // argv[3-4] => pipes + + int pipe1[2]; // written by host process, read by plugin UI process + int pipe2[2]; // written by plugin UI process, read by host process + + if (pipe(pipe1) != 0) + { + fail("pipe1 creation failed"); + return; + } + + if (pipe(pipe2) != 0) + { + fail("pipe2 creation failed"); + return; + } + + char uiPipeRecv[100+1]; + char uiPipeSend[100+1]; + + std::snprintf(uiPipeRecv, 100, "%d", pipe1[0]); /* [0] means reading end */ + std::snprintf(uiPipeSend, 100, "%d", pipe2[1]); /* [1] means writting end */ + + uiPipeRecv[100] = '\0'; + uiPipeSend[100] = '\0'; + + argv[3] = uiPipeRecv; // reading end + argv[4] = uiPipeSend; // writting end + + //---------------------------------------------------------------- + // argv[5] => NULL + + argv[5] = nullptr; + + //---------------------------------------------------------------- + // fork + + int ret = -1; + + if ((! fork_exec(argv, &ret)) || ret == -1) + { + close(pipe1[0]); + close(pipe1[1]); + close(pipe2[0]); + close(pipe2[1]); + fail("fork_exec() failed"); + return; + } + + fPid = ret; + + /* fork duplicated the handles, close pipe ends that are used by the child process */ + close(pipe1[0]); + close(pipe2[1]); + + fPipeSend = pipe1[1]; /* [1] means writting end */ + fPipeRecv = pipe2[0]; /* [0] means reading end */ + + fcntl(fPipeRecv, F_SETFL, fcntl(fPipeRecv, F_GETFL) | O_NONBLOCK); + + //---------------------------------------------------------------- + // wait a while for child process to confirm it is alive + + char ch; + + for (int i=0; ;) + { + ret = read(fPipeRecv, &ch, 1); + + switch (ret) + { + case -1: + if (errno == EAGAIN) + { + if (i < WAIT_START_TIMEOUT / WAIT_STEP) + { + carla_msleep(WAIT_STEP); + i++; + continue; + } + + carla_stderr("we have waited for child with pid %d to appear for %.1f seconds and we are giving up", (int)fPid, (float)WAIT_START_TIMEOUT / 1000.0f); + } + else + carla_stderr("read() failed: %s", strerror(errno)); + break; + + case 1: + if (ch == '\n') + // success + return; + + carla_stderr("read() wrong first char '%c'", ch); + break; + + default: + carla_stderr("read() returned %d", ret); + break; + } + + break; + } + + carla_stderr("force killing misbehaved child %d (start)", (int)fPid); + + if (kill(fPid, SIGKILL) == -1) + { + carla_stderr("kill() failed: %s (start)\n", strerror(errno)); + } + + /* wait a while child to exit, we dont like zombie processes */ + wait_child(fPid); + } + + void stop() + { + carla_debug("CarlaPipeServer::stop()"); + + if (fPipeSend == -1 || fPipeRecv == -1 || fPid == -1) + return; + + write(fPipeSend, "quit\n", 5); + + waitChildClose(); + + close(fPipeRecv); + close(fPipeSend); + fPipeRecv = -1; + fPipeSend = -1; + fPid = -1; + } + + void idle() + { + char* locale = nullptr; + + for (;;) + { + char* const msg = readline(); + + if (msg == nullptr) + break; + + if (locale == nullptr) + { + locale = strdup(setlocale(LC_NUMERIC, nullptr)); + setlocale(LC_NUMERIC, "POSIX"); + } + + fReading = true; + msgReceived(msg); + fReading = false; + + std::free(msg); + } + + if (locale != nullptr) + { + setlocale(LC_NUMERIC, locale); + std::free(locale); + } + } + + // ------------------------------------------------------------------- + + bool readNextLineAsBool(bool& value) + { + if (! fReading) + return false; + + if (char* const msg = readline()) + { + value = (std::strcmp(msg, "true") == 0); + std::free(msg); + return true; + } + + return false; + } + + bool readNextLineAsInt(int& value) + { + if (! fReading) + return false; + + if (char* const msg = readline()) + { + value = atoi(msg); + std::free(msg); + return true; + } + + return false; + } + + bool readNextLineAsFloat(float& value) + { + if (! fReading) + return false; + + if (char* const msg = readline()) + { + bool ret = (sscanf(msg, "%f", &value) == 1); + std::free(msg); + return ret; + } + + return false; + } + + bool readNextLineAsString(char*& value) + { + if (! fReading) + return false; + + if (char* const msg = readline()) + { + value = msg; + return true; + } + + return false; + } + + void writeMsg(const char* const msg) + { + ::write(fPipeSend, msg, std::strlen(msg)); + } + + void writeMsg(const char* const msg, size_t size) + { + ::write(fPipeSend, msg, size); + } + + void writeAndFixMsg(const char* const msg) + { + const size_t size = std::strlen(msg); + + char smsg[size+1]; + std::strcpy(smsg, msg); + + smsg[size-1] = '\n'; + smsg[size] = '\0'; + + for (size_t i=0; i