| From 108513fce9904acf6d7d291ab2b87fc8909a621a Mon Sep 17 00:00:00 2001 |
| From: Stephen Foulds <stephen.foulds@sky.uk> |
| Date: Thu, 8 Sep 2022 12:06:21 +0100 |
| Subject: [PATCH] RDKTV-15347: Add PluginActivator utility for systemd |
| integration (#1059) |
| |
| * Add PluginActivator utility for systemd integration |
| |
| * Update copyright headers |
| |
| * Code style changes to match project style |
| |
| * Minor formatting fix |
| |
| * Address code review comments |
| --- |
| CMakeLists.txt | 7 + |
| Utils/PluginActivator/CMakeLists.txt | 42 +++++ |
| Utils/PluginActivator/README.md | 18 ++ |
| .../PluginActivator/source/COMRPCStarter.cpp | 154 ++++++++++++++++++ |
| Utils/PluginActivator/source/COMRPCStarter.h | 47 ++++++ |
| Utils/PluginActivator/source/IPluginStarter.h | 43 +++++ |
| Utils/PluginActivator/source/Log.h | 68 ++++++++ |
| Utils/PluginActivator/source/Module.cpp | 23 +++ |
| Utils/PluginActivator/source/Module.h | 31 ++++ |
| Utils/PluginActivator/source/main.cpp | 132 +++++++++++++++ |
| 10 files changed, 565 insertions(+) |
| create mode 100644 Utils/PluginActivator/CMakeLists.txt |
| create mode 100644 Utils/PluginActivator/README.md |
| create mode 100644 Utils/PluginActivator/source/COMRPCStarter.cpp |
| create mode 100644 Utils/PluginActivator/source/COMRPCStarter.h |
| create mode 100755 Utils/PluginActivator/source/IPluginStarter.h |
| create mode 100644 Utils/PluginActivator/source/Log.h |
| create mode 100644 Utils/PluginActivator/source/Module.cpp |
| create mode 100644 Utils/PluginActivator/source/Module.h |
| create mode 100644 Utils/PluginActivator/source/main.cpp |
| |
| diff --git a/CMakeLists.txt b/CMakeLists.txt |
| index 4ba768b1..3e9004d2 100644 |
| --- a/CMakeLists.txt |
| +++ b/CMakeLists.txt |
| @@ -27,6 +27,9 @@ set(VERSION ${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}) |
| |
| message(STATUS "Setting up ${PROJECT_NAME} v${VERSION}") |
| |
| +option(BUILD_PLUGIN_ACTIVATOR |
| + "Build the standalone plugin activator utility to activate plugins using systemd" OFF) |
| + |
| if (BUILD_REFERENCE) |
| add_definitions (-DBUILD_REFERENCE=${BUILD_REFERENCE}) |
| endif() |
| @@ -96,3 +99,7 @@ endif() |
| |
| add_subdirectory(Source) |
| add_subdirectory(Tests) |
| + |
| +if (BUILD_PLUGIN_ACTIVATOR) |
| + add_subdirectory(Utils/PluginActivator) |
| +endif() |
| diff --git a/Utils/PluginActivator/CMakeLists.txt b/Utils/PluginActivator/CMakeLists.txt |
| new file mode 100644 |
| index 00000000..bd465e3a |
| --- /dev/null |
| +++ b/Utils/PluginActivator/CMakeLists.txt |
| @@ -0,0 +1,42 @@ |
| + |
| +# If not stated otherwise in this file or this component's license file the |
| +# following copyright and licenses apply: |
| +# |
| +# Copyright 2020 Metrological |
| +# |
| +# Licensed under the Apache License, Version 2.0 (the "License"); |
| +# you may not use this file except in compliance with the License. |
| +# You may obtain a copy of the License at |
| +# |
| +# http://www.apache.org/licenses/LICENSE-2.0 |
| +# |
| +# Unless required by applicable law or agreed to in writing, software |
| +# distributed under the License is distributed on an "AS IS" BASIS, |
| +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| +# See the License for the specific language governing permissions and |
| +# limitations under the License. |
| + |
| +cmake_minimum_required(VERSION 3.10.3) |
| + |
| +project( PluginActivator ) |
| + |
| +set( CMAKE_CXX_STANDARD 11 ) |
| + |
| +add_executable(${PROJECT_NAME} |
| + source/Module.cpp |
| + source/main.cpp |
| + source/COMRPCStarter.cpp |
| +) |
| + |
| +target_link_libraries(${PROJECT_NAME} |
| + PRIVATE |
| + CompileSettingsDebug::CompileSettingsDebug |
| + ${NAMESPACE}Core::${NAMESPACE}Core |
| + ${NAMESPACE}COM::${NAMESPACE}COM |
| + ${NAMESPACE}Plugins::${NAMESPACE}Plugins |
| +) |
| + |
| +install( |
| + TARGETS ${PROJECT_NAME} |
| + RUNTIME DESTINATION bin |
| +) |
| diff --git a/Utils/PluginActivator/README.md b/Utils/PluginActivator/README.md |
| new file mode 100644 |
| index 00000000..0ab0cff6 |
| --- /dev/null |
| +++ b/Utils/PluginActivator/README.md |
| @@ -0,0 +1,18 @@ |
| +# Plugin Activator |
| +A command-line tool to activate a specified Thunder plugin. |
| + |
| +Will automatically retry activation mutliple times if the plugin does not successfully activate. |
| + |
| +Designed to be used with systemd where each plugin becomes a standalone systemd service, activated with this tool. |
| + |
| +## Usage |
| +``` |
| +Usage: PluginActivator <option(s)> [callsign] |
| + Utility that starts a given thunder plugin |
| + |
| + -h, --help Print this help and exit |
| + -r, --retries Maximum amount of retries to attempt to start the plugin before giving up |
| + -d, --delay Delay (in ms) between each attempt to start the plugin if it fails |
| + |
| + [callsign] Callsign of the plugin to activate (Required) |
| +``` |
| \ No newline at end of file |
| diff --git a/Utils/PluginActivator/source/COMRPCStarter.cpp b/Utils/PluginActivator/source/COMRPCStarter.cpp |
| new file mode 100644 |
| index 00000000..88f50558 |
| --- /dev/null |
| +++ b/Utils/PluginActivator/source/COMRPCStarter.cpp |
| @@ -0,0 +1,154 @@ |
| +/* |
| + * If not stated otherwise in this file or this component's LICENSE file the |
| + * following copyright and licenses apply: |
| + * |
| + * Copyright 2020 Metrological |
| + * |
| + * Licensed under the Apache License, Version 2.0 (the "License"); |
| + * you may not use this file except in compliance with the License. |
| + * You may obtain a copy of the License at |
| + * |
| + * http://www.apache.org/licenses/LICENSE-2.0 |
| + * |
| + * Unless required by applicable law or agreed to in writing, software |
| + * distributed under the License is distributed on an "AS IS" BASIS, |
| + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| + * See the License for the specific language governing permissions and |
| + * limitations under the License. |
| + */ |
| + |
| +#include "COMRPCStarter.h" |
| + |
| +#include "Log.h" |
| + |
| +#include <chrono> |
| +#include <thread> |
| + |
| +COMRPCStarter::COMRPCStarter(const string& pluginName) |
| + : _pluginName(pluginName) |
| + , _engine(Core::ProxyType<RPC::InvokeServerType<1, 0, 4>>::Create()) |
| + , _client(Core::ProxyType<RPC::CommunicatorClient>::Create(getConnectionEndpoint(), |
| + Core::ProxyType<Core::IIPCServer>(_engine))) |
| +{ |
| + // Announce our arrival |
| + _engine->Announcements(_client->Announcement()); |
| + |
| + if (!_client.IsValid()) { |
| + LOG_ERROR(pluginName.c_str(), "Failed to create valid COM-RPC client"); |
| + } |
| +} |
| + |
| +COMRPCStarter::~COMRPCStarter() |
| +{ |
| + // Close the connection just in case something left it open |
| + if (_client->IsOpen()) { |
| + _client->Close(RPC::CommunicationTimeOut); |
| + } |
| + |
| + if (_client.IsValid()) { |
| + _client.Release(); |
| + } |
| +} |
| + |
| +/** |
| + * @brief Attempt to activate the plugin and automatically retry on failure |
| + * |
| + * @param[in] maxRetries Maximum amount of times to retry activation if it fails |
| + * @param[in] retryDelayMs Delay in ms between retry attempts |
| + * |
| + * @return True if plugin successfully activated, false if failed to activate |
| + */ |
| +bool COMRPCStarter::activatePlugin(const uint8_t maxRetries, const uint16_t retryDelayMs) |
| +{ |
| + // Attempt to open the plugin shell |
| + bool success = false; |
| + int currentRetry = 1; |
| + |
| + PluginHost::IShell* shell = nullptr; |
| + |
| + while (!success && currentRetry <= maxRetries) { |
| + LOG_INF(_pluginName.c_str(), "Attempting to activate plugin - attempt %d/%d", currentRetry, maxRetries); |
| + |
| + auto start = Core::Time::Now(); |
| + |
| + if (!shell) { |
| + shell = _client->Open<PluginHost::IShell>(_pluginName, ~0, RPC::CommunicationTimeOut); |
| + } |
| + |
| + // We could not open IShell for some reason - Thunder doesn't give an error about why this might have failed |
| + // Normally this is because either: |
| + // a) Thunder isn't running or we can't connect to /tmp/communicator |
| + // b) A plugin with the specified callsign does not exist |
| + if (!shell) { |
| + LOG_ERROR(_pluginName.c_str(), "Failed to open IShell interface for %s", _pluginName.c_str()); |
| + currentRetry++; |
| + |
| + // Must close the connection before we retry, since even if shell is null the underlying |
| + // connection will still have been opened |
| + _client->Close(RPC::CommunicationTimeOut); |
| + |
| + // Sleep, then try again |
| + std::this_thread::sleep_for(std::chrono::milliseconds(retryDelayMs)); |
| + } else { |
| + // Connected to Thunder successfully and got the plugin shell |
| + if (shell->State() == PluginHost::IShell::ACTIVATED) { |
| + LOG_INF(_pluginName.c_str(), "Plugin is already activated - nothing to do"); |
| + success = true; |
| + } else { |
| + // Will block until plugin is activated |
| + uint32_t result = shell->Activate(PluginHost::IShell::REQUESTED); |
| + |
| + auto duration = Core::Time::Now().Sub(start.MilliSeconds()); |
| + |
| + if (result != Core::ERROR_NONE) { |
| + if (result == Core::ERROR_PENDING_CONDITIONS) { |
| + // Ideally we'd print out which preconditions are un-met for debugging, but that data is not exposed through the IShell interface |
| + LOG_ERROR(_pluginName.c_str(), "Failed to activate plugin due to unmet preconditions after %dms", duration.MilliSeconds()); |
| + } else { |
| + LOG_ERROR(_pluginName.c_str(), "Failed to activate plugin with error %u (%s) after %dms", result, Core::ErrorToString(result), duration.MilliSeconds()); |
| + } |
| + |
| + // Try activation again up until the max number of retries |
| + currentRetry++; |
| + std::this_thread::sleep_for(std::chrono::milliseconds(retryDelayMs)); |
| + } else { |
| + // Our work here is done! |
| + LOG_INF(_pluginName.c_str(), "Successfully activated plugin after %dms", duration.MilliSeconds()); |
| + success = true; |
| + } |
| + } |
| + } |
| + } |
| + |
| + if (!success) { |
| + LOG_ERROR(_pluginName.c_str(), "Max retries hit - giving up activating the plugin"); |
| + } |
| + |
| + if (shell) { |
| + shell->Release(); |
| + } |
| + |
| + // Be a good citizen and don't leave any open connections |
| + if (_client->IsOpen()) { |
| + _client->Close(RPC::CommunicationTimeOut); |
| + } |
| + |
| + return success; |
| +} |
| + |
| +/** |
| + * Retrieves the socket we will communicate over for COM-RPC |
| + */ |
| +Core::NodeId COMRPCStarter::getConnectionEndpoint() const |
| +{ |
| + string communicatorPath; |
| + Core::SystemInfo::GetEnvironment(_T("COMMUNICATOR_PATH"), communicatorPath); |
| + |
| + // On linux, Thunder defaults to /tmp/communicator for the generic COM-RPC |
| + // interface |
| + if (communicatorPath.empty()) { |
| + communicatorPath = _T("/tmp/communicator"); |
| + } |
| + |
| + return Core::NodeId(communicatorPath.c_str()); |
| +} |
| \ No newline at end of file |
| diff --git a/Utils/PluginActivator/source/COMRPCStarter.h b/Utils/PluginActivator/source/COMRPCStarter.h |
| new file mode 100644 |
| index 00000000..dec8fa46 |
| --- /dev/null |
| +++ b/Utils/PluginActivator/source/COMRPCStarter.h |
| @@ -0,0 +1,47 @@ |
| +/* |
| + * If not stated otherwise in this file or this component's LICENSE file the |
| + * following copyright and licenses apply: |
| + * |
| + * Copyright 2020 Metrological |
| + * |
| + * Licensed under the Apache License, Version 2.0 (the "License"); |
| + * you may not use this file except in compliance with the License. |
| + * You may obtain a copy of the License at |
| + * |
| + * http://www.apache.org/licenses/LICENSE-2.0 |
| + * |
| + * Unless required by applicable law or agreed to in writing, software |
| + * distributed under the License is distributed on an "AS IS" BASIS, |
| + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| + * See the License for the specific language governing permissions and |
| + * limitations under the License. |
| + */ |
| + |
| +#pragma once |
| +#include "Module.h" |
| + |
| +#include "IPluginStarter.h" |
| + |
| +using namespace WPEFramework; |
| + |
| +/** |
| + * @brief COM-RPC implementation of a plugin starter |
| + * |
| + * Connects to Thunder over COM-RPC and attempts to start a given plugin |
| + */ |
| +class COMRPCStarter : public IPluginStarter { |
| +public: |
| + explicit COMRPCStarter(const string& pluginName); |
| + ~COMRPCStarter() override; |
| + |
| + bool activatePlugin(const uint8_t maxRetries, const uint16_t retryDelayMs) override; |
| + |
| +private: |
| + Core::NodeId getConnectionEndpoint() const; |
| + |
| +private: |
| + const string _pluginName; |
| + |
| + Core::ProxyType<RPC::InvokeServerType<1, 0, 4>> _engine; |
| + Core::ProxyType<RPC::CommunicatorClient> _client; |
| +}; |
| \ No newline at end of file |
| diff --git a/Utils/PluginActivator/source/IPluginStarter.h b/Utils/PluginActivator/source/IPluginStarter.h |
| new file mode 100755 |
| index 00000000..32128417 |
| --- /dev/null |
| +++ b/Utils/PluginActivator/source/IPluginStarter.h |
| @@ -0,0 +1,43 @@ |
| +/* |
| + * If not stated otherwise in this file or this component's LICENSE file the |
| + * following copyright and licenses apply: |
| + * |
| + * Copyright 2020 Metrological |
| + * |
| + * Licensed under the Apache License, Version 2.0 (the "License"); |
| + * you may not use this file except in compliance with the License. |
| + * You may obtain a copy of the License at |
| + * |
| + * http://www.apache.org/licenses/LICENSE-2.0 |
| + * |
| + * Unless required by applicable law or agreed to in writing, software |
| + * distributed under the License is distributed on an "AS IS" BASIS, |
| + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| + * See the License for the specific language governing permissions and |
| + * limitations under the License. |
| + */ |
| + |
| +#pragma once |
| + |
| +#include <string> |
| + |
| +/** |
| + * Interface to start a specified plugin |
| + * |
| + * Could be implemented with JSON-RPC or COM-RPC |
| + */ |
| +class IPluginStarter { |
| +public: |
| + virtual ~IPluginStarter() = default; |
| + |
| + /** |
| + * @brief Activate a Thunder plugin |
| + * |
| + * Will block until either the Thunder plugin has activated or until the maximum amount of retires has occurred |
| + * |
| + * @param[in] maxRetries Maximum amount of attempts to activate the plugin - if plugin is not activated within the amount of retries |
| + * this method will return false |
| + * @param[in] retryDelayMs Amount of time to wait after a failed activation before retrying again |
| + */ |
| + virtual bool activatePlugin(const uint8_t maxRetries, const uint16_t retryDelayMs) = 0; |
| +}; |
| \ No newline at end of file |
| diff --git a/Utils/PluginActivator/source/Log.h b/Utils/PluginActivator/source/Log.h |
| new file mode 100644 |
| index 00000000..87dff0b2 |
| --- /dev/null |
| +++ b/Utils/PluginActivator/source/Log.h |
| @@ -0,0 +1,68 @@ |
| +/* |
| + * If not stated otherwise in this file or this component's LICENSE file the |
| + * following copyright and licenses apply: |
| + * |
| + * Copyright 2020 Metrological |
| + * |
| + * Licensed under the Apache License, Version 2.0 (the "License"); |
| + * you may not use this file except in compliance with the License. |
| + * You may obtain a copy of the License at |
| + * |
| + * http://www.apache.org/licenses/LICENSE-2.0 |
| + * |
| + * Unless required by applicable law or agreed to in writing, software |
| + * distributed under the License is distributed on an "AS IS" BASIS, |
| + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| + * See the License for the specific language governing permissions and |
| + * limitations under the License. |
| + */ |
| + |
| +#pragma once |
| + |
| +#include <stdio.h> |
| +#include <string.h> |
| + |
| +#define LOG_LEVEL 3 |
| + |
| +#ifndef __FILENAME__ |
| +#define __FILENAME__ (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__) |
| +#endif |
| + |
| +#define LEVEL_DEBUG 3 |
| +#define LEVEL_INFO 2 |
| +#define LEVEL_WARN 1 |
| +#define LEVEL_ERROR 0 |
| + |
| +#define LOG_DBG(plugin, fmt, ...) \ |
| + __LOG(LEVEL_DEBUG, plugin, fmt, ##__VA_ARGS__) |
| + |
| +#define LOG_INF(plugin, fmt, ...) \ |
| + __LOG(LEVEL_INFO, plugin, fmt, ##__VA_ARGS__) |
| + |
| +#define LOG_WARN(plugin, fmt, ...) \ |
| + __LOG(LEVEL_WARN, plugin, fmt, ##__VA_ARGS__) |
| + |
| +#define LOG_ERROR(plugin, fmt, ...) \ |
| + __LOG(LEVEL_ERROR, plugin, fmt, ##__VA_ARGS__) |
| + |
| +#define __LOG(level, plugin, fmt, ...) \ |
| + do { \ |
| + if (__builtin_expect(((level) <= LOG_LEVEL), 0)) \ |
| + fprintf(stderr, "%s[%s:%d][%s] (%s) " fmt "\n", getLogLevel(level), __FILENAME__, __LINE__, __FUNCTION__, plugin, ##__VA_ARGS__); \ |
| + } while (0) |
| + |
| +inline const char* getLogLevel(int level) |
| +{ |
| + switch (level) { |
| + case LEVEL_DEBUG: |
| + return "[DBG]"; |
| + case LEVEL_INFO: |
| + return "[NFO]"; |
| + case LEVEL_WARN: |
| + return "[WRN]"; |
| + case LEVEL_ERROR: |
| + return "[ERR]"; |
| + default: |
| + return ""; |
| + } |
| +} |
| \ No newline at end of file |
| diff --git a/Utils/PluginActivator/source/Module.cpp b/Utils/PluginActivator/source/Module.cpp |
| new file mode 100644 |
| index 00000000..aa310602 |
| --- /dev/null |
| +++ b/Utils/PluginActivator/source/Module.cpp |
| @@ -0,0 +1,23 @@ |
| +/* |
| + * If not stated otherwise in this file or this component's LICENSE file the |
| + * following copyright and licenses apply: |
| + * |
| + * Copyright 2020 Metrological |
| + * |
| + * Licensed under the Apache License, Version 2.0 (the "License"); |
| + * you may not use this file except in compliance with the License. |
| + * You may obtain a copy of the License at |
| + * |
| + * http://www.apache.org/licenses/LICENSE-2.0 |
| + * |
| + * Unless required by applicable law or agreed to in writing, software |
| + * distributed under the License is distributed on an "AS IS" BASIS, |
| + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| + * See the License for the specific language governing permissions and |
| + * limitations under the License. |
| + */ |
| + |
| +#include "Module.h" |
| + |
| +// If not set in CMake flags, BUILD_REFERENCE defaults to "engineering_build_for_debug_purpose_only" |
| +MODULE_NAME_DECLARATION(BUILD_REFERENCE) |
| \ No newline at end of file |
| diff --git a/Utils/PluginActivator/source/Module.h b/Utils/PluginActivator/source/Module.h |
| new file mode 100644 |
| index 00000000..b2500eec |
| --- /dev/null |
| +++ b/Utils/PluginActivator/source/Module.h |
| @@ -0,0 +1,31 @@ |
| +/* |
| + * If not stated otherwise in this file or this component's LICENSE file the |
| + * following copyright and licenses apply: |
| + * |
| + * Copyright 2020 Metrological |
| + * |
| + * Licensed under the Apache License, Version 2.0 (the "License"); |
| + * you may not use this file except in compliance with the License. |
| + * You may obtain a copy of the License at |
| + * |
| + * http://www.apache.org/licenses/LICENSE-2.0 |
| + * |
| + * Unless required by applicable law or agreed to in writing, software |
| + * distributed under the License is distributed on an "AS IS" BASIS, |
| + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| + * See the License for the specific language governing permissions and |
| + * limitations under the License. |
| + */ |
| + |
| +#pragma once |
| + |
| +#ifndef MODULE_NAME |
| +#define MODULE_NAME PluginStarter |
| +#endif |
| + |
| +#include <com/com.h> |
| +#include <core/core.h> |
| +#include <plugins/plugins.h> |
| + |
| +#undef EXTERNAL |
| +#define EXTERNAL |
| diff --git a/Utils/PluginActivator/source/main.cpp b/Utils/PluginActivator/source/main.cpp |
| new file mode 100644 |
| index 00000000..fa1ac9dd |
| --- /dev/null |
| +++ b/Utils/PluginActivator/source/main.cpp |
| @@ -0,0 +1,132 @@ |
| +/* |
| + * If not stated otherwise in this file or this component's LICENSE file the |
| + * following copyright and licenses apply: |
| + * |
| + * Copyright 2020 Metrological |
| + * |
| + * Licensed under the Apache License, Version 2.0 (the "License"); |
| + * you may not use this file except in compliance with the License. |
| + * You may obtain a copy of the License at |
| + * |
| + * http://www.apache.org/licenses/LICENSE-2.0 |
| + * |
| + * Unless required by applicable law or agreed to in writing, software |
| + * distributed under the License is distributed on an "AS IS" BASIS, |
| + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| + * See the License for the specific language governing permissions and |
| + * limitations under the License. |
| + */ |
| + |
| +#include "COMRPCStarter.h" |
| +#include "Module.h" |
| + |
| +#include <memory> |
| + |
| +static int gRetryCount = 100; |
| +static int gRetryDelayMs = 500; |
| +static string gPluginName; |
| + |
| +/** |
| + * @brief Display a help message for the tool |
| + */ |
| +static void displayUsage() |
| +{ |
| + printf("Usage: PluginActivator <option(s)> [callsign]\n"); |
| + printf(" Utility that starts a given thunder plugin\n\n"); |
| + printf(" -h, --help Print this help and exit\n"); |
| + printf(" -r, --retries Maximum amount of retries to attempt to start the plugin before giving up\n"); |
| + printf(" -d, --delay Delay (in ms) between each attempt to start the plugin if it fails\n"); |
| + printf("\n"); |
| + printf(" [callsign] Callsign of the plugin to activate (Required)\n"); |
| +} |
| + |
| +/** |
| + * @brief Parse the provided command line arguments |
| + * |
| + * Must be given the name of the plugin to activate, everything else |
| + * is optional and will fallback to sane defaults |
| + */ |
| +static void parseArgs(const int argc, char** argv) |
| +{ |
| + if (argc == 1) { |
| + displayUsage(); |
| + exit(EXIT_SUCCESS); |
| + } |
| + |
| + struct option longopts[] = { |
| + { "help", no_argument, nullptr, (int)'h' }, |
| + { "retries", required_argument, nullptr, (int)'r' }, |
| + { "delay", required_argument, nullptr, (int)'d' }, |
| + { nullptr, 0, nullptr, 0 } |
| + }; |
| + |
| + opterr = 0; |
| + |
| + int option; |
| + int longindex; |
| + |
| + while ((option = getopt_long(argc, argv, "hr:d:", longopts, &longindex)) != -1) { |
| + switch (option) { |
| + case 'h': |
| + displayUsage(); |
| + exit(EXIT_SUCCESS); |
| + break; |
| + case 'r': |
| + gRetryCount = std::atoi(optarg); |
| + if (gRetryCount < 0) { |
| + fprintf(stderr, "Error: Retry count must be > 0\n"); |
| + exit(EXIT_FAILURE); |
| + } |
| + break; |
| + case 'd': |
| + gRetryDelayMs = std::atoi(optarg); |
| + if (gRetryDelayMs < 0) { |
| + fprintf(stderr, "Error: Delay ms must be > 0\n"); |
| + exit(EXIT_FAILURE); |
| + } |
| + break; |
| + case '?': |
| + if (optopt == 'c') |
| + fprintf(stderr, "Warning: Option -%c requires an argument.\n", optopt); |
| + else if (isprint(optopt)) |
| + fprintf(stderr, "Warning: Unknown option `-%c'.\n", optopt); |
| + else |
| + fprintf(stderr, "Warning: Unknown option character `\\x%x'.\n", optopt); |
| + |
| + exit(EXIT_FAILURE); |
| + break; |
| + default: |
| + exit(EXIT_FAILURE); |
| + break; |
| + } |
| + } |
| + |
| + if (optind == argc) { |
| + fprintf(stderr, "Error: Must provide plugin name to activate\n"); |
| + exit(EXIT_FAILURE); |
| + } |
| + |
| + gPluginName = argv[optind]; |
| + |
| + optind++; |
| + for (int i = optind; i < argc; i++) { |
| + printf("Warning: Non-option argument %s ignored\n", argv[i]); |
| + } |
| +} |
| + |
| +int main(int argc, char* argv[]) |
| +{ |
| + parseArgs(argc, argv); |
| + |
| + // For now, we only implement the starter in COM-RPC but could do a JSON-RPC version |
| + // in the future |
| + bool success; |
| + std::unique_ptr<IPluginStarter> starter(new COMRPCStarter(gPluginName)); |
| + success = starter->activatePlugin(gRetryCount, gRetryDelayMs); |
| + |
| + // Destruct the COM-RPC starter so it cleans up after itself before we dispose WPEFramework singletons |
| + starter.reset(); |
| + Core::Singleton::Dispose(); |
| + |
| + return success ? EXIT_SUCCESS : EXIT_FAILURE; |
| +} |
| \ No newline at end of file |
| -- |
| 2.34.1 |
| |