extlibs/mbedtls/mbedtls
extlibs/raxmpp/raxmpp
extlibs/yaml/yaml
+extlibs/rapidjson/rapidjson
# Ignore editor (e.g. Emacs) backup and autosave files
*~
$ sudo apt-get install git-core scons ssh build-essential g++ doxygen valgrind
Install external libraries:
- $ sudo apt-get install libboost-dev libboost-program-options-dev libboost-thread-dev uuid-dev libssl-dev libtool libglib2.0-dev
+ $ sudo apt-get install libboost-dev libboost-program-options-dev libboost-thread-dev uuid-dev libssl-dev libtool libglib2.0-dev libcap-dev libcurl4-openssl-dev autotools-dev autoconf
Build release binaries:
$ scons
# Build "plugin interface" sub-project
SConscript(build_dir + 'plugins/SConscript')
+# Build "bridging" sub-project
+SConscript(build_dir + 'bridging/SConscript')
+
# Append targets information to the help information, to see help info, execute command line:
# $ scon [options] -h
env.PrintTargets()
--- /dev/null
+#******************************************************************
+#
+# Copyright 2017 Intel Mobile Communications GmbH All Rights Reserved.
+#
+#-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+#
+# 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.
+#
+#-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+##
+# Bridging build script
+##
+
+import os.path
+
+Import('env')
+
+target_os = env.get('TARGET_OS')
+build_sample = 'ON'
+src_dir = env.get('SRC_DIR')
+
+# rapidjson fetch
+SConscript(os.path.join(env.get('SRC_DIR'), 'extlibs', 'rapidjson', 'SConscript'))
+
+if target_os not in ['android', 'arduino', 'darwin', 'ios', 'tizen', 'msys_nt', 'windows']:
+
+ SConscript(os.path.join('common', 'SConscript'))
+
+ SConscript(os.path.join('mini_plugin_manager', 'SConscript'))
+
+ SConscript(os.path.join('mpm_client', 'SConscript'))
+
+ SConscript(os.path.join('plugins', 'lifx_plugin', 'SConscript'))
+
+# SConscript(os.path.join('plugins', 'hue_plugin', 'SConscript'))
+
+# SConscript(os.path.join('plugins', 'nest_plugin', 'SConscript'))
+
+# SConscript(os.path.join('plugins', 'lyric_plugin', 'SConscript'))
--- /dev/null
+//******************************************************************
+//
+// Copyright 2017 Intel Mobile Communications GmbH All Rights Reserved.
+//
+//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+//
+// 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 <sstream>
+#include "octypes.h"
+#include "ConcurrentIotivityUtils.h"
+#include "ocpayload.h"
+#include "logger.h"
+
+#define TAG "CONCURRENT_IOTIVITY_UTILS"
+
+using namespace OC::Bridging;
+
+// Static member initializations.
+std::unique_ptr<WorkQueue<std::unique_ptr<IotivityWorkItem>>> ConcurrentIotivityUtils::m_queue;
+
+void ConcurrentIotivityUtils::startWorkerThreads()
+{
+ if (m_threadStarted)
+ {
+ throw "Work Queue Processor already started";
+ }
+ m_processWorkQueueThread = std::thread(&ConcurrentIotivityUtils::processWorkQueue, this);
+ m_ocProcessThread = std::thread(&ConcurrentIotivityUtils::callOCProcess, this);
+ m_threadStarted = true;
+}
+
+void ConcurrentIotivityUtils::stopWorkerThreads()
+{
+ m_shutDownOCProcessThread = true;
+ m_queue->shutdown();
+ m_processWorkQueueThread.join();
+ m_ocProcessThread.join();
+ m_threadStarted = false;
+}
+
+OCStackResult ConcurrentIotivityUtils::queueCreateResource(const std::string &uri,
+ const std::string &resourceType,
+ const std::string &interface, OCEntityHandler entityHandler,
+ void *callbackParam, uint8_t resourceProperties)
+{
+ std::unique_ptr<IotivityWorkItem> item = make_unique<CreateResourceItem>(
+ uri, resourceType, interface, entityHandler, callbackParam, resourceProperties
+ );
+
+ m_queue->put(std::move(item));
+
+ return OC_STACK_OK;
+}
+
+OCStackResult ConcurrentIotivityUtils::respondToRequest(OCEntityHandlerRequest *request,
+ OCRepPayload *payload, OCEntityHandlerResult responseCode)
+{
+ std::unique_ptr<OCEntityHandlerResponse> response = make_unique<OCEntityHandlerResponse>();
+
+ response->requestHandle = request->requestHandle;
+ response->resourceHandle = request->resource;
+ response->ehResult = responseCode;
+
+ // Clone a copy since this allocation is going across thread boundaries.
+ response->payload = (OCPayload *) OCRepPayloadClone(payload);
+
+ if (payload != NULL && response->payload == NULL)
+ {
+ return OC_STACK_NO_MEMORY;
+ }
+
+ std::unique_ptr<IotivityWorkItem> item = make_unique<SendResponseItem>(std::move(response));
+ m_queue->put(std::move(item));
+
+ return OC_STACK_OK;
+}
+
+OCStackResult ConcurrentIotivityUtils::respondToRequestWithError(OCEntityHandlerRequest *request,
+ const std::string &errorMessage,
+ OCEntityHandlerResult errorCode)
+{
+ OCRepPayload *errorPayload = NULL;
+
+ if (!errorMessage.empty())
+ {
+ errorPayload = OCRepPayloadCreate();
+
+ if (!errorPayload)
+ {
+ return OC_STACK_NO_MEMORY;
+ }
+
+ OCRepPayloadSetPropString(errorPayload, "x.org.iotivity.error", errorMessage.c_str());
+ }
+
+ OCStackResult res = respondToRequest(request, errorPayload, errorCode);
+
+ if (errorPayload)
+ {
+ OCRepPayloadDestroy(errorPayload);
+ }
+
+ return res;
+}
+
+OCStackResult ConcurrentIotivityUtils::queueNotifyObservers(const std::string &resourceUri)
+{
+ std::unique_ptr<IotivityWorkItem> item = make_unique<NotifyObserversItem>(resourceUri);
+ m_queue->put(std::move(item));
+ return OC_STACK_OK;
+}
+
+OCStackResult ConcurrentIotivityUtils::queueDeleteResource(const std::string &uri)
+{
+ std::unique_ptr<IotivityWorkItem> item = make_unique<DeleteResourceItem>(uri);
+ m_queue->put(std::move(item));
+ return OC_STACK_OK;
+}
+
+bool ConcurrentIotivityUtils::getUriFromHandle(OCResourceHandle handle, std::string &uri)
+{
+ const char *uri_c = OCGetResourceUri(handle);
+
+ if (uri_c == NULL)
+ {
+ return false;
+ }
+
+ uri = uri_c;
+ return true;
+}
+
+void ConcurrentIotivityUtils::getKeyValueParams(const std::string &query,
+ std::map<std::string, std::string> &keyValueMap)
+{
+ if (query.empty())
+ {
+ return;
+ }
+
+ std::stringstream ss(query);
+
+ std::string keyValuePair;
+
+ while (std::getline(ss, keyValuePair, '&'))
+ {
+ auto keyValueSeparator = keyValuePair.find('=');
+
+ if (keyValueSeparator == std::string::npos)
+ {
+ continue;
+ }
+
+ std::string key = keyValuePair.substr(0, keyValueSeparator);
+ std::string value = keyValuePair.substr(keyValueSeparator + 1);
+
+ keyValueMap[key] = value;
+ }
+}
+
+bool ConcurrentIotivityUtils::isRequestForDefaultInterface(const std::string &query)
+{
+ if (query.empty())
+ {
+ return false;
+ }
+ std::map<std::string, std::string> keyValueParams;
+
+ getKeyValueParams(query, keyValueParams);
+
+ auto it = keyValueParams.find(OC_RSRVD_INTERFACE);
+
+ if (it == keyValueParams.end())
+ {
+ return false;
+ }
+
+ return it->second == OC_RSRVD_INTERFACE_DEFAULT;
+}
--- /dev/null
+#******************************************************************
+#
+# Copyright 2017 Intel Mobile Communications GmbH All Rights Reserved.
+#
+#-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+#
+# 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.
+#
+#-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+##
+# MPM Common Plugin build script
+##
+
+import os
+import os.path
+
+Import('env')
+
+target_os = env.get('TARGET_OS')
+src_dir = env.get('SRC_DIR')
+bridging_path = os.path.join(src_dir, 'bridging')
+
+mpmcommon_env = env.Clone()
+
+print "Reading MPM Common Plugin script"
+
+def maskFlags(flags):
+ flags = [flags.replace('-Wl,--no-undefined', '' ) for flags in flags]
+ return flags
+
+######################################################################
+# Build flags
+######################################################################
+mpmcommon_env.PrependUnique(CPPPATH = [
+ os.path.join(src_dir, 'resource', 'include'),
+ ])
+mpmcommon_env.AppendUnique(CPPPATH = [ os.path.join(bridging_path, 'include'),
+ ])
+
+if target_os not in ['arduino', 'windows']:
+ mpmcommon_env.AppendUnique(CPPDEFINES = ['WITH_POSIX'])
+ mpmcommon_env.AppendUnique(CXXFLAGS = ['-std=c++0x', '-Wall', '-Wextra', '-Werror', '-fpic'])
+
+if target_os in ['darwin','ios']:
+ mpmcommon_env.AppendUnique(CPPDEFINES = ['_DARWIN_C_SOURCE'])
+
+mpmcommon_env.AppendUnique(RPATH = [mpmcommon_env.get('BUILD_DIR')])
+mpmcommon_env.AppendUnique(LIBPATH = [mpmcommon_env.get('BUILD_DIR')])
+mpmcommon_env['LINKFLAGS'] = maskFlags(env['LINKFLAGS'])
+
+if mpmcommon_env.get('LOGGING'):
+ mpmcommon_env.AppendUnique(CPPDEFINES = ['TB_LOG'])
+
+mpmcommon_env.PrependUnique(LIBS = ['m',
+ 'octbstack',
+ 'ocsrm',
+ 'connectivity_abstraction',
+ 'coap',
+ 'curl' ])
+
+#####################################################################
+# Source files and Target(s)
+######################################################################
+mpmcommon_src = [
+ os.path.join(bridging_path, 'common', 'pluginIf.cpp'),
+ os.path.join(bridging_path, 'common', 'pluginServer.cpp'),
+ os.path.join(bridging_path, 'common', 'pipeHandler.cpp'),
+ os.path.join(bridging_path, 'common', 'messageHandler.cpp'),
+ os.path.join(bridging_path, 'common', 'curlClient.cpp'),
+ os.path.join(bridging_path, 'common', 'pluginProcess.cpp'),
+ os.path.join(bridging_path, 'common', 'ConcurrentIotivityUtils.cpp')
+ ]
+
+mpmcommon_env.AppendUnique(MPMCOMMON_SRC = mpmcommon_src)
+mpmcommonlib = mpmcommon_env.StaticLibrary('mpmcommon', mpmcommon_env.get('MPMCOMMON_SRC'))
+mpmcommon_env.InstallTarget(mpmcommonlib, 'mpmcommon')
+mpmcommon_env.UserInstallTargetLib(mpmcommonlib, 'mpmcommon')
--- /dev/null
+//******************************************************************
+//
+// Copyright 2017 Intel Mobile Communications GmbH All Rights Reserved.
+//
+//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+//
+// 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 "curlClient.h"
+#include <iostream>
+#include "logger.h"
+
+using namespace std;
+using namespace OC::Bridging;
+
+#define TAG "CURL_CLIENT"
+
+#define DEFAULT_CURL_TIMEOUT_SECONDS 60L
+
+
+size_t CurlClient::WriteCallback(void *contents, size_t size, size_t nmemb, void *userp)
+{
+ size_t realsize = size * nmemb;
+ MemoryChunk *mem = static_cast<MemoryChunk *>(userp);
+
+ mem->memory = static_cast<char *>(realloc(mem->memory, mem->size + realsize + 1));
+ if (mem->memory == NULL)
+ {
+ OIC_LOG(ERROR, TAG, "not enough memory!");
+ return 0;
+ }
+
+ memcpy(&(mem->memory[mem->size]), contents, realsize);
+ mem->size += realsize;
+ mem->memory[mem->size] = 0;
+
+ return realsize;
+}
+
+int CurlClient::decomposeHeader(const char *header, std::vector<std::string> &headers)
+{
+ size_t npos = 0;
+ if (NULL == header)
+ {
+ return MPM_RESULT_INVALID_PARAMETER;
+ }
+
+ std::string header_s = header;
+
+ npos = header_s.find("\r\n");
+ while (npos != std::string::npos)
+ {
+ std::string s = header_s.substr(0, npos);
+ headers.push_back(s);
+ header_s = header_s.substr(npos + 2);
+ npos = header_s.find("\r\n");
+ }
+
+ return MPM_RESULT_OK;
+}
+
+int CurlClient::doInternalRequest(const std::string &url,
+ const std::string &method,
+ const std::vector<std::string> &inHeaders,
+ const std::string &request,
+ const std::string &username,
+ std::vector<std::string> &outHeaders,
+ std::string &response)
+{
+ int result = MPM_RESULT_OK;
+ CURL *curl = NULL;
+ CURLcode res = CURLE_OK;
+ struct curl_slist *headers = NULL;
+ MemoryChunk rsp_body;
+ MemoryChunk rsp_header;
+ m_lastResponseCode = INVALID_RESPONSE_CODE; //initialize recorded code value in case of
+ //early return
+
+ curl = curl_easy_init();
+ if (curl != NULL)
+ {
+ curl_easy_reset(curl);
+
+ for (unsigned int i = 0; i < inHeaders.size(); i++)
+ {
+ headers = curl_slist_append(headers, inHeaders[i].c_str());
+ if (NULL == headers)
+ {
+ OIC_LOG(ERROR, TAG, "curl_slist_append failed");
+ result = MPM_RESULT_OUT_OF_MEMORY;
+ goto CLEANUP;
+ }
+ }
+
+ // Expect the transfer to complete within DEFAULT_CURL_TIMEOUT seconds
+ curl_easy_setopt(curl, CURLOPT_TIMEOUT, DEFAULT_CURL_TIMEOUT_SECONDS);
+
+ // Set CURLOPT_VERBOSE to 1L below to see detailed debugging
+ // information on curl operations.
+ curl_easy_setopt(curl, CURLOPT_VERBOSE, 0);
+ curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
+ curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
+ curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
+ curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, false);
+ curl_easy_setopt(curl, CURLOPT_POSTFIELDS, request.c_str());
+ curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
+ curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, WriteCallback);
+ curl_easy_setopt(curl, CURLOPT_WRITEDATA, &rsp_body);
+ curl_easy_setopt(curl, CURLOPT_HEADERDATA, &rsp_header);
+ if (CURLUSESSL_NONE != m_useSsl)
+ {
+ curl_easy_setopt(curl, CURLOPT_USE_SSL, m_useSsl);
+ }
+
+ if (!username.empty())
+ {
+ curl_easy_setopt(curl, CURLOPT_USERNAME, username.c_str());
+ }
+
+ if (!method.empty())
+ {
+ // NOTE: The documentation for CURLOPT_CUSTOMREQUEST only lists HTTP, FTP, IMAP, POP3, and SMTP
+ // as valid options, although it says all this option does is change the string used in
+ // the request. (Basically, don't know whether this option has any effect as currently
+ // used?
+
+ /// only required for GET, PUT, DELETE
+ curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, method.c_str());
+ }
+
+ res = curl_easy_perform(curl);
+ if (res != CURLE_OK)
+ {
+ OIC_LOG_V(ERROR, TAG, "curl_easy_perform failed with %lu", (unsigned long) res);
+ result = MPM_RESULT_NETWORK_ERROR;
+ goto CLEANUP;
+ }
+
+ if (CURLE_OK != curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &m_lastResponseCode))
+ {
+ OIC_LOG(WARNING, TAG, "curl_easy_getinfo(CURLINFO_RESPONSE_CODE) failed.");
+ m_lastResponseCode = INVALID_RESPONSE_CODE;
+ }
+
+ response = rsp_body.memory;
+
+ decomposeHeader(rsp_header.memory, outHeaders);
+ }
+ else
+ {
+ OIC_LOG(ERROR, TAG, "curl_easy_init failed");
+ result = MPM_RESULT_INTERNAL_ERROR;
+ }
+
+CLEANUP:
+ if (NULL != headers)
+ {
+ curl_slist_free_all(headers);
+ }
+
+ free(rsp_body.memory);
+
+ free(rsp_header.memory);
+
+ if (NULL != curl)
+ {
+ curl_easy_cleanup(curl);
+ }
+
+ return result;
+}
--- /dev/null
+//******************************************************************
+//
+// Copyright 2017 Intel Mobile Communications GmbH All Rights Reserved.
+//
+//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+//
+// 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.
+//
+//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+//
+
+/* This file contains the plugin server implementation. Most of this
+ * implementation is reused for other plugins. There is NO customization
+ * required of functions in this file to accommodate plugin specific code.
+ */
+
+
+#include "iotivity_config.h"
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include "oic_malloc.h"
+#include "pluginIf.h"
+#include "pluginServer.h"
+#include "cbor.h"
+#include "logger.h"
+#include "StringConstants.h"
+
+#define TAG "MESSAGE_HANDLER"
+
+#define NAME "NAME"
+#define MANUFACTURER "MF"
+#define DEVICETYPE "DEVICE_TYPE"
+#define PLUGINSPECIFICDETAILS "PluginSpecificDetails"
+#define RESOURCES "RESOURCES"
+
+#define VERIFY_CBOR_SUCCESS(log_tag, err, log_message) \
+ if ((CborNoError != (err)) && (CborErrorOutOfMemory != (err))) \
+ { \
+ if ((log_tag) && (log_message)) \
+ { \
+ OIC_LOG_V(ERROR, TAG, "cbor error - %s \n", log_message); \
+ } \
+ } \
+
+MPMCommonPluginCtx *g_com_ctx;
+
+void MPMRequestHandler(MPMPipeMessage *pipe_message, MPMPluginCtx *ctx)
+{
+ OIC_LOG(DEBUG, TAG, "Inside request_handler");
+
+ switch (pipe_message->msgType)
+ {
+ case MPM_SCAN:
+ OIC_LOG(DEBUG, TAG, "plugin_scan called");
+ pluginScan(ctx, pipe_message);
+ break;
+ case MPM_ADD:
+ OIC_LOG(DEBUG, TAG, "plugin_add called");
+ pluginAdd(ctx, pipe_message);
+ break;
+ case MPM_REMOVE:
+ OIC_LOG(DEBUG, TAG, "plugin_remove called");
+ pluginRemove(ctx, pipe_message);
+ break;
+ case MPM_RECONNECT:
+ OIC_LOG(DEBUG, TAG, "Plugin reconnect called");
+ pluginReconnect(ctx, pipe_message);
+ break;
+ default:OIC_LOG(DEBUG, TAG, "Currently not supported");
+ break;
+ }
+}
+
+MPMResult MPMSendResponse(const void *response, size_t size, MPMMessageType type)
+{
+ MPMResult result = MPM_RESULT_INTERNAL_ERROR;
+
+ OIC_LOG_V(DEBUG, TAG, "Inside response_handler and size of payload = %d", (int)size);
+
+ MPMPipeMessage pipe_message;
+
+ pipe_message.payloadSize = size;
+ pipe_message.msgType = type;
+ pipe_message.payload = (uint8_t *)response;
+
+ result = MPMWritePipeMessage(g_com_ctx->parent_reads_fds.write_fd, &pipe_message);
+
+ return result;
+}
+
+static int64_t AddTextStringToMap(CborEncoder *map, const char *key, size_t keylen,
+ const char *value)
+{
+ int64_t err = cbor_encode_text_string(map, key, keylen);
+ if (CborNoError != err)
+ {
+ return err;
+ }
+ return cbor_encode_text_string(map, value, strlen(value));
+}
+
+static int64_t AddstructureToMap(CborEncoder *map, const char *key, size_t keylen,
+ const char *value, size_t valueSize)
+{
+ int64_t err = cbor_encode_text_string(map, key, keylen);
+ if (CborNoError != err)
+ {
+ return err;
+ }
+ return cbor_encode_text_string(map, value, valueSize);
+}
+
+int64_t MPMFormMetaData(MPMResourceList *list, MPMDeviceSpecificData *deviceDetails,
+ uint8_t *buff, size_t size, void *details, size_t payloadSize)
+{
+
+ CborEncoder encoder;
+ int64_t err = CborNoError;
+ CborEncoder rootArray, rootMap, linkMap;
+ CborEncoder linkArray;
+ MPMResourceList *temp = NULL;
+
+ cbor_encoder_init(&encoder, buff, size, 0);
+
+ err = cbor_encoder_create_array(&encoder, &rootArray, 1);
+ VERIFY_CBOR_SUCCESS(TAG, err, " Creating Root Array");
+
+ err = cbor_encoder_create_map(&rootArray, &rootMap, CborIndefiniteLength);
+ VERIFY_CBOR_SUCCESS(TAG, err, "Creating Root MAP");
+
+ if (deviceDetails)
+ {
+ err = AddTextStringToMap(&rootMap, NAME, sizeof(NAME) - 1,
+ deviceDetails->devName);
+ VERIFY_CBOR_SUCCESS(TAG, err, "Adding device name");
+
+ err = AddTextStringToMap(&rootMap, MANUFACTURER, sizeof(MANUFACTURER) - 1,
+ deviceDetails->manufacturerName);
+ VERIFY_CBOR_SUCCESS(TAG, err, "Adding Manufacture name");
+
+ err = AddTextStringToMap(&rootMap, DEVICETYPE, sizeof(DEVICETYPE) - 1,
+ deviceDetails->devType);
+ VERIFY_CBOR_SUCCESS(TAG, err, "Adding Device Type");
+ }
+
+ if (details)
+ {
+ err = AddstructureToMap(&rootMap, PLUGINSPECIFICDETAILS, sizeof(PLUGINSPECIFICDETAILS) - 1,
+ (const char *)details, payloadSize);
+ VERIFY_CBOR_SUCCESS(TAG, err, "Adding Plugin specific Details");
+ }
+
+ err = cbor_encode_text_string(&rootMap, RESOURCES, sizeof(RESOURCES) - 1);
+ VERIFY_CBOR_SUCCESS(TAG, err, "Encoding Resources string");
+
+ err = cbor_encoder_create_array(&rootMap, &linkArray, CborIndefiniteLength);
+ VERIFY_CBOR_SUCCESS(TAG, err, "Creating Link Array");
+
+ for ( ; list ; )
+ {
+ temp = list;
+ OIC_LOG_V(DEBUG, TAG, " href - %s\n rt - %s\n if - %s\n bm - %d\n", list->href, list->rt,
+ list->interfaces, list->bitmap);
+ // resource map inside the links array.
+ err = cbor_encoder_create_map(&linkArray, &linkMap, 4);
+ VERIFY_CBOR_SUCCESS(TAG, err, "Creating Link Map");
+
+ err = AddTextStringToMap(&linkMap, OC::Key::RESOURCETYPESKEY.c_str(),
+ OC::Key::RESOURCETYPESKEY.size(),
+ list->rt);
+ VERIFY_CBOR_SUCCESS(TAG, err, "Adding Resource type");
+
+ err = AddTextStringToMap(&linkMap, OC::Key::URIKEY.c_str(), OC::Key::URIKEY.size(),
+ list->href);
+ VERIFY_CBOR_SUCCESS(TAG, err, "Adding OC::Key::URIKEY");
+
+ err = AddTextStringToMap(&linkMap, OC::Key::INTERFACESKEY.c_str(),
+ OC::Key::INTERFACESKEY.size(), list->interfaces);
+ VERIFY_CBOR_SUCCESS(TAG, err, "Adding Resource Interface");
+
+ err = cbor_encode_text_string(&linkMap, OC::Key::BMKEY.c_str(), OC::Key::BMKEY.size());
+ VERIFY_CBOR_SUCCESS(TAG, err, "Encoding Bitmap string");
+
+ err = cbor_encode_int(&linkMap, list->bitmap);
+ VERIFY_CBOR_SUCCESS(TAG, err, "encoding bit map");
+
+ // close link map inside link array
+ err = cbor_encoder_close_container(&linkArray, &linkMap);
+ VERIFY_CBOR_SUCCESS(TAG, err, "Closing link map");
+
+ list = list -> next;
+ OICFree(temp);
+ }
+
+ // Close links array inside the root map.
+ err = cbor_encoder_close_container(&rootMap, &linkArray);
+ VERIFY_CBOR_SUCCESS(TAG, err, "Closing link array");
+
+ // close root map inside the root array.
+ err = cbor_encoder_close_container(&rootArray, &rootMap);
+ VERIFY_CBOR_SUCCESS(TAG, err, "Closing Root Map");
+
+ // Close the final root array.
+ err = cbor_encoder_close_container(&encoder, &rootArray);
+ VERIFY_CBOR_SUCCESS(TAG, err, "Closing root Array");
+
+ return err;
+}
+
+void MPMParseMetaData(const uint8_t *buff, size_t size, MPMResourceList **list, void **details)
+{
+ int64_t err = CborNoError;
+ CborValue rootMapValue, linkMapValue;
+ CborValue resourceMapValue;
+ CborValue curVal;
+ CborValue rootValue;
+ CborParser parser;
+ int bitmap;
+
+ err = cbor_parser_init(buff, size, 0, &parser, &rootValue);
+ VERIFY_CBOR_SUCCESS(TAG, err, "Parser cbor init");
+
+ if (cbor_value_is_array(&rootValue))
+ {
+ OIC_LOG_V(DEBUG, TAG, "ENCODED DATA - %s ", (char *)buff);
+ err = cbor_value_enter_container(&rootValue, &rootMapValue);
+ VERIFY_CBOR_SUCCESS(TAG, err, "Entering root array");
+ if (!cbor_value_is_map(&rootMapValue))
+ {
+ OIC_LOG(ERROR, TAG, "ERROR, Malformed packet");
+ return ;
+ }
+
+ if (cbor_value_is_map(&rootMapValue))
+ {
+ // Parsing device details
+ err = cbor_value_map_find_value(&rootMapValue, NAME, &curVal);
+ VERIFY_CBOR_SUCCESS(TAG, err, "finding Name in map");
+ if (cbor_value_is_valid(&curVal))
+ {
+ if (cbor_value_is_text_string(&curVal))
+ {
+ size_t len = 0;
+ char *input = NULL;
+ err = cbor_value_dup_text_string(&curVal, &input, &len, NULL);
+ VERIFY_CBOR_SUCCESS(TAG, err, "Duplicating name string");
+ OIC_LOG_V(DEBUG, TAG, "\"NAME\":%s\n", input);
+ free(input);
+ }
+ }
+ }
+
+ err = cbor_value_map_find_value(&rootMapValue, MANUFACTURER, &curVal);
+ VERIFY_CBOR_SUCCESS(TAG, err, "Finding Manufacturer details in map");
+ if (cbor_value_is_valid(&curVal))
+ {
+ if (cbor_value_is_text_string(&curVal))
+ {
+ size_t len = 0;
+ char *input = NULL;
+ err = cbor_value_dup_text_string(&curVal, &input, &len, NULL);
+ VERIFY_CBOR_SUCCESS(TAG, err, " Copying Text string");
+ OIC_LOG_V(DEBUG, TAG, "\"MF\":%s\n", input);
+ free(input);
+ }
+ }
+
+ err = cbor_value_map_find_value(&rootMapValue, PLUGINSPECIFICDETAILS, &curVal);
+ VERIFY_CBOR_SUCCESS(TAG, err, " Finding PLUGINSPECIFICDETAILS in map ");
+ if (cbor_value_is_valid(&curVal))
+ {
+ if (cbor_value_is_text_string(&curVal))
+ {
+ size_t len = 0;
+ char *input = NULL;
+ err = cbor_value_dup_text_string(&curVal, &input, &len, NULL);
+ VERIFY_CBOR_SUCCESS(TAG, err, " Copying Text string");
+ *details = (void *)input;
+ }
+ }
+
+ err = cbor_value_map_find_value(&rootMapValue, RESOURCES, &linkMapValue);
+ VERIFY_CBOR_SUCCESS(TAG, err, " Finding RESOURCES in map ");
+ // Enter the links array and start iterating through the array processing
+ // each resource which shows up as a map.
+ if (cbor_value_is_valid(&linkMapValue))
+ {
+
+ err = cbor_value_enter_container(&linkMapValue, &resourceMapValue);
+ VERIFY_CBOR_SUCCESS(TAG, err, " Entering Link map ");
+ while (cbor_value_is_map(&resourceMapValue))
+ {
+ MPMResourceList *tempPtr;
+ tempPtr = (MPMResourceList *) OICCalloc(1, sizeof(MPMResourceList));
+ if (tempPtr == NULL)
+ {
+ OIC_LOG(ERROR, TAG, "calloc failed");
+ return;
+ }
+ size_t len = 0;
+ char *input = NULL;
+ err = cbor_value_map_find_value(&resourceMapValue, OC::Key::URIKEY.c_str(), &curVal);
+ VERIFY_CBOR_SUCCESS(TAG, err, " Finding Uri in map ");
+
+ err = cbor_value_dup_text_string(&curVal, &input, &len, NULL);
+ VERIFY_CBOR_SUCCESS(TAG, err, " Copying Text string");
+ strncpy(tempPtr->href, input, MPM_MAX_LENGTH_64);
+ OIC_LOG_V(DEBUG, TAG, "\"ref\":%s\n", input);
+ free(input);
+ input = NULL;
+
+ err = cbor_value_map_find_value(&resourceMapValue, OC::Key::RESOURCETYPESKEY.c_str(), &curVal);
+ VERIFY_CBOR_SUCCESS(TAG, err, " Finding Rt in link map ");
+ err = cbor_value_dup_text_string(&curVal, &input, &len, NULL);
+ VERIFY_CBOR_SUCCESS(TAG, err, " Copying Text string");
+ strncpy(tempPtr->rt, input, MPM_MAX_LENGTH_64);
+ OIC_LOG_V(DEBUG, TAG, "\"rt\":%s\n", input);
+ free(input);
+ input = NULL;
+
+ err = cbor_value_map_find_value(&resourceMapValue, OC::Key::INTERFACESKEY.c_str(), &curVal);
+ VERIFY_CBOR_SUCCESS(TAG, err, " Finding If's in link map ");
+ err = cbor_value_dup_text_string(&curVal, &input, &len, NULL);
+ VERIFY_CBOR_SUCCESS(TAG, err, " Copying Text string");
+ strncpy(tempPtr->interfaces, input, MPM_MAX_LENGTH_64);
+ OIC_LOG_V(DEBUG, TAG, "\"if\":%s\n", input);
+ free(input);
+ input = NULL;
+
+ err = cbor_value_map_find_value(&resourceMapValue, OC::Key::BMKEY.c_str(), &curVal);
+ VERIFY_CBOR_SUCCESS(TAG, err, " Finding Bms in link map ");
+ if (cbor_value_is_integer(&curVal))
+ {
+ err = cbor_value_get_int(&curVal, &bitmap);
+ VERIFY_CBOR_SUCCESS(TAG, err, " Getting bit map value fromx link map ");
+ tempPtr->bitmap = bitmap;
+ OIC_LOG_V(DEBUG, TAG, "\"bm\":%d\n", bitmap);
+ }
+
+ tempPtr->next = *list;
+ *list = tempPtr;
+ err = cbor_value_advance(&resourceMapValue);
+ VERIFY_CBOR_SUCCESS(TAG, err, "in resource map value advance");
+ }
+ }
+ }
+}
--- /dev/null
+//******************************************************************
+//
+// Copyright 2017 Intel Mobile Communications GmbH All Rights Reserved.
+//
+//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+//
+// 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.
+//
+//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+//
+
+/**
+ * This file is mainly intended for writing and reading the messages over the pipe,
+ * This is the common code functionality where either client or MPM can call these
+ * functions to write to the pipe and read from the pipe.
+ */
+
+#include <string.h>
+#include <errno.h>
+#include "messageHandler.h"
+#include "iotivity_config.h"
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include "platform_features.h"
+#include "oic_malloc.h"
+#include "logger.h"
+#include <cinttypes>
+
+#define TAG "PIPE_HANDLER"
+
+MPMResult MPMWritePipeMessage(int fd, const MPMPipeMessage *pipe_message)
+{
+ ssize_t ret = 0;
+ OIC_LOG(DEBUG, TAG, "writing message over pipe");
+
+ OIC_LOG_V(DEBUG, TAG, "Message type = %d, payload size = %" PRIuPTR, pipe_message->msgType,
+ pipe_message->payloadSize);
+
+ ret = write(fd, &pipe_message->payloadSize, sizeof(size_t));
+ if (ret < 0)
+ {
+ OIC_LOG_V(ERROR, TAG, "Error writing message over the pipe - [%s]", strerror(errno));
+ return MPM_RESULT_INTERNAL_ERROR;
+ }
+
+ ret = write(fd, &pipe_message->msgType, sizeof(MPMMessageType));
+ if (ret < 0)
+ {
+ OIC_LOG_V(ERROR, TAG, "Error writing message over the pipe - [%s]", strerror(errno));
+ return MPM_RESULT_INTERNAL_ERROR;
+ }
+
+ if (pipe_message->payloadSize > 0)
+ {
+ ret = write(fd, pipe_message->payload, pipe_message->payloadSize);
+
+ if (ret < 0)
+ {
+ OIC_LOG_V(ERROR, TAG, "Error writing message over the pipe - [%s]", strerror(errno));
+ return MPM_RESULT_INTERNAL_ERROR;
+ }
+ }
+
+ return MPM_RESULT_OK;
+}
+
+
+ssize_t MPMReadPipeMessage(int fd, MPMPipeMessage *pipe_message)
+{
+ ssize_t ret = 0, bytesRead =0;
+ OIC_LOG(DEBUG, TAG, "reading message from pipe");
+ OIC_LOG_V(DEBUG, TAG, "Message type = %d, payload size = %" PRIuPTR , pipe_message->msgType,
+ pipe_message->payloadSize);
+
+ ret = read(fd, &pipe_message->payloadSize, sizeof(size_t));
+ if (ret < 0)
+ {
+ OIC_LOG_V(ERROR, TAG, "Error Reading message from the pipe - [%s]", strerror(errno));
+ return ret;
+ }
+ bytesRead = ret;
+
+ ret = read(fd, &pipe_message->msgType, sizeof(MPMMessageType));
+ if (ret < 0)
+ {
+ OIC_LOG_V(ERROR, TAG, "Error Reading message from the pipe - [%s]", strerror(errno));
+ return ret;
+ }
+ bytesRead += ret;
+
+
+ if (pipe_message->msgType == MPM_NOMSG)
+ {
+ bytesRead = 0;
+ }
+ else if (pipe_message->payloadSize > 0 && pipe_message->payloadSize <= SIZE_MAX)
+ {
+ pipe_message->payload = (uint8_t *) OICCalloc(1, pipe_message->payloadSize);
+ if (!pipe_message->payload)
+ {
+ OIC_LOG(ERROR, TAG, "failed to allocate memory");
+ bytesRead = 0;
+ }
+ else
+ {
+ ret = read(fd, (void*)pipe_message->payload, pipe_message->payloadSize);
+ if (ret < 0)
+ {
+ OIC_LOG_V(ERROR, TAG, "Error Reading message from the pipe - [%s]", strerror(errno));
+ return ret;
+ }
+ bytesRead += ret;
+ }
+ }
+ else
+ {
+ pipe_message->payload = NULL;
+ OIC_LOG(DEBUG, TAG, "no payload received");
+ }
+ return bytesRead;
+}
--- /dev/null
+//******************************************************************
+//
+// Copyright 2017 Intel Mobile Communications GmbH All Rights Reserved.
+//
+//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+//
+// 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.
+//
+//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+//
+
+/* This file contains the "C" plugin interface implementation */
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <spawn.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/select.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include "messageHandler.h"
+#include "logger.h"
+#include "oic_malloc.h"
+#include "mpmErrorCode.h"
+#include "pluginIf.h"
+#include "pluginServer.h"
+
+#define TAG "PLUGIN_IF"
+
+/* this function waits for a child process to signal that it is complete
+ * @param[in] child_pid child process id
+ * @param[in] timeout time to wait for child to complete
+ */
+static void waitForChildProcessToComplete(pid_t child_pid, int32_t timeout);
+
+
+/* This is a timed wait for pipe write; the written value is returned in the passed
+ * in message buffer.
+ * @param[in] fd_to_parent File descriptor
+ * @param[out] message Message from the child
+ * @param[in] timeout Time to wait for pipe write in seconds
+ */
+static void timedWaitForPipeWrite(int fd_to_parent, MPMPipeMessage *message,
+ int32_t timeout);
+
+
+/**
+ * The purpose of this function is to create a context or instance of the
+ * plugin. There is nothing stopping one from creating more than one
+ * instance of the plugin. Currently it is expected that only one instance
+ * is created.
+ *
+ * @return A pointer to the context data of this plugin that is managed
+ * by this plugin.
+ */
+static MPMCommonPluginCtx *create()
+{
+ /* always try to create a new instance */
+ MPMCommonPluginCtx *ctx = (MPMCommonPluginCtx *) OICCalloc(1, sizeof(MPMCommonPluginCtx));
+
+ if (ctx == NULL)
+ {
+ OIC_LOG(ERROR, TAG, "Unable to allocate context.");
+ }
+ return ctx;
+}
+
+/**
+ * This function forks a new process and starts an OCF server in that
+ * process. The plan is to only allow one fork per instance of
+ * the plugin. There is a check for this, the start will fail if you
+ * call start twice on the same instance.
+ *
+ * @param[in] ctx the plugin context created with the "create" function
+ *
+ * @return 0 on success, 1 on error
+ */
+static int start(MPMCommonPluginCtx *ctx)
+{
+ int result = 1;
+
+ if (ctx && !ctx->started)
+ {
+ pid_t pid;
+ MPMPipeMessage pipe_message;
+
+ pipe_message.payloadSize = 0;
+ pipe_message.msgType = MPM_NOMSG;
+ pipe_message.payload = NULL;
+
+ /* Create unnamed pipes for IPC prior to the fork so that both
+ * parent and child have the same information on the unnamed
+ * pipes.
+ */
+ int parent_result = pipe(&(ctx->parent_reads_fds.read_fd));
+ if (parent_result == -1)
+ {
+ OIC_LOG(ERROR, TAG, "Failed to create IPC unnamed pipe for parent.");
+ return result;
+ }
+ int child_result = pipe(&(ctx->child_reads_fds.read_fd));
+
+ if (child_result == -1)
+ {
+ OIC_LOG(ERROR, TAG, "Failed to create IPC unnamed pipe for child.");
+ close(ctx->parent_reads_fds.read_fd);
+ close(ctx->parent_reads_fds.write_fd);
+ return result;
+ }
+
+ switch (pid = fork())
+ {
+ case 0:
+ /* Child(plugin) process.
+ * Close unused edges of unnamed pipes from the
+ * child's perspective. Child is not going
+ * write to the child's read pipe nor is the child
+ * going to read from the parent's read pipe
+ */
+ close(ctx->child_reads_fds.write_fd);
+ close(ctx->parent_reads_fds.read_fd);
+
+ /* Start the OCF server. This is a blocking call and will
+ return only when the plugin stops*/
+ MPMPluginService(ctx);
+
+ OIC_LOG(INFO, TAG, "Child process complete.");
+
+ /* Close the other sides of the pipes from
+ * the child's perspective
+ */
+ close(ctx->child_reads_fds.read_fd);
+ close(ctx->parent_reads_fds.write_fd);
+
+ exit(0);
+ break;
+
+ default:
+
+ /* Parent process */
+ ctx->child_pid = pid;
+
+ /* The parent is not going to read
+ * from the child's read pipe and the parent is not
+ * going to write the parents read pipe.
+ */
+ close(ctx->child_reads_fds.read_fd);
+ close(ctx->parent_reads_fds.write_fd);
+
+ /* The plugin may fail to create or start.
+ * The parent must wait here for some time to
+ * learn what happened.
+ */
+ timedWaitForPipeWrite(ctx->parent_reads_fds.read_fd,
+ &pipe_message,
+ MPM_TIMEOUT_VAL_IN_SEC);
+ if (pipe_message.msgType == MPM_DONE)
+ {
+ /* plugin was successful with its create and start */
+ OIC_LOG_V(INFO, TAG, "Child: %d started successfully", ctx->child_pid);
+ result = 0;
+ ctx->started = true;
+ }
+ else
+ {
+ /* We have a problem, and the plugin is NOT going to load.
+ */
+ OIC_LOG_V(ERROR, TAG, "Child: %d failed, either plugin create or start.",
+ ctx->child_pid);
+
+ OIC_LOG(ERROR, TAG, "Forced completion of the child");
+
+ /* The parent must wait here until the child process is dead.
+ * but this time we are not going to wait, we are only going
+ * to force completion
+ */
+ waitForChildProcessToComplete(ctx->child_pid, 0);
+
+ /* Let's close the rest of the pipe interfaces. Sides of the pipes
+ * from the parent's perspective
+ */
+ close(ctx->child_reads_fds.write_fd);
+ close(ctx->parent_reads_fds.read_fd);
+ }
+
+ OICFree((void*)pipe_message.payload);
+
+ break;
+
+ case -1:
+ perror("fork");
+ OIC_LOG(ERROR, TAG, "Fork returned error.");
+ break;
+ }
+ }
+ else
+ {
+ OIC_LOG(ERROR, TAG, "Plugin instance already forked or has not been created.");
+ }
+ return (result);
+}
+
+/**
+ * This function writes a stop message over the pipe to indicate the child
+ * to stop
+ *
+ * @param[in] ctx the plugin context created with the "create" function
+ */
+static void stop(MPMCommonPluginCtx *ctx)
+{
+ MPMResult result = MPM_RESULT_INTERNAL_ERROR;
+
+ if (ctx && ctx->started)
+ {
+ /* IPC message variable */
+ MPMPipeMessage pipe_message;
+
+ pipe_message.payloadSize = 0;
+ pipe_message.msgType = MPM_STOP;
+ pipe_message.payload = NULL;
+
+ result = MPMWritePipeMessage(ctx->child_reads_fds.write_fd, &pipe_message);
+ if (result != MPM_RESULT_OK)
+ {
+ OIC_LOG(ERROR, TAG, "Failed to write to pipe for stop");
+ return;
+ }
+ OIC_LOG_V(INFO, TAG, "Parent telling the child process pid: %d to stop.", ctx->child_pid);
+
+ /* the parent must wait here until the child process is dead */
+ waitForChildProcessToComplete(ctx->child_pid, MPM_TIMEOUT_VAL_IN_SEC);
+
+ ctx->started = false;
+ }
+ else
+ {
+ OIC_LOG(INFO, TAG, "Stop has no effect; Plugin has not been started");
+ }
+ return;
+}
+
+/**
+ * This function calls the "stop" function follwed by destruction of the
+ * context created with the "create" function
+ *
+ * @param[in] ctx the plugin context created with the "create" function
+ */
+static void destroy(MPMCommonPluginCtx *ctx)
+{
+ if (ctx && ctx->started)
+ {
+ stop(ctx);
+ }
+ OICFree(ctx);
+}
+
+static void waitForChildProcessToComplete(pid_t child_pid, int32_t timeout)
+{
+ int32_t waittime = 0;
+ int status = 0;
+ pid_t wpid = 0;
+
+ do
+ {
+ wpid = waitpid(child_pid, &status, WNOHANG);
+ if (wpid == 0)
+ {
+ if (waittime < timeout)
+ {
+ OIC_LOG_V(INFO, TAG, "Parent waiting on child: %d second(s): %d",
+ child_pid, waittime);
+ sleep(1);
+ waittime++;
+ }
+ else
+ {
+ OIC_LOG_V(INFO, TAG, "Parent forced stopping of child: %d", child_pid);
+ kill(child_pid, SIGKILL);
+ break;
+ }
+ }
+ }
+ while ((wpid == 0) && (waittime <= timeout));
+
+ if (WIFEXITED(status))
+ {
+ OIC_LOG_V(INFO, TAG, "Child: %d completed \"exited\" with status: %d",
+ child_pid, WEXITSTATUS(status));
+ }
+ else if (WIFSIGNALED(status))
+ {
+ OIC_LOG_V(INFO, TAG, "Child: %d completed \"signaled\" with status: %d",
+ child_pid, WTERMSIG(status));
+ }
+}
+
+static void timedWaitForPipeWrite(int fd, MPMPipeMessage *msg, int32_t timeout)
+{
+ if (NULL != msg)
+ {
+ struct timeval tv;
+ fd_set fdset;
+ int nfd = -1;
+ int32_t waittime = 0;
+ ssize_t nbytes = 0;
+
+ /* wait for 1 second on each time through the loop for up to timeout */
+ tv.tv_sec = 0;
+ tv.tv_usec = 0;
+
+ do
+ {
+ FD_ZERO(&(fdset));
+ FD_SET(fd, &(fdset));
+ sleep(1); /* tried to set the timeout on select and it was not reliable */
+ nfd = select(fd + 1, &(fdset), NULL, NULL, &tv);
+ if (nfd == -1)
+ {
+ OIC_LOG_V(ERROR, TAG, "select error :[%s]", strerror(errno));
+ break;
+ }
+ else if (nfd)
+ {
+ if (FD_ISSET(fd, &(fdset)))
+ {
+ nbytes = MPMReadPipeMessage(fd, msg);
+ }
+ }
+ else
+ {
+ /* got nothing case */
+ OIC_LOG_V(INFO, TAG, "Parent waiting on child seconds(s): %d", waittime);
+ }
+
+ waittime++;
+
+ }
+ while ((nbytes == 0) && (waittime <= timeout));
+ }
+}
+
+typedef MPMCommonPluginCtx *(*create_t)();
+typedef int (*start_t)(MPMCommonPluginCtx *ctx);
+typedef void (*stop_t)(MPMCommonPluginCtx *ctx);
+typedef void (*destroy_t)(MPMCommonPluginCtx *ctx);
+
+typedef struct plugin_runtime_tag
+{
+ create_t create;
+ start_t start;
+ stop_t stop;
+ destroy_t destroy;
+} MPMPluginRuntime;
+
+// This is the symbol table that will be loaded by the mini plugin manager.
+// This is the "interface" between the mini plugin manager and the plugin.
+MPMPluginRuntime plugin_funcs =
+{
+ create,
+ start,
+ stop,
+ destroy
+};
+
--- /dev/null
+//******************************************************************
+//
+// Copyright 2017 Intel Mobile Communications GmbH All Rights Reserved.
+//
+//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+//
+// 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 "pluginServer.h"
+
+void MPMPluginSpecificProcess(void)
+{
+}
--- /dev/null
+//******************************************************************
+//
+// Copyright 2017 Intel Mobile Communications GmbH All Rights Reserved.
+//
+//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+//
+// 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.
+//
+//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+//
+
+/* This file contains the plugin server implementation. Most of this
+ * implementation is reused for other plugins. There is NO customization
+ * required of functions in this file to accommodate plugin specific code.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include "iotivity_config.h"
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include "messageHandler.h"
+#include "pluginServer.h"
+#include "oic_malloc.h"
+#include "pluginIf.h"
+#include "ocpayload.h"
+#include "logger.h"
+#include "WorkQueue.h"
+#include "ConcurrentIotivityUtils.h"
+#include <iostream>
+#include <octypes.h>
+
+#define TAG "PLUGIN_SERVER"
+#define OC_KEY_VALUE_DELIMITER "="
+
+using namespace OC::Bridging;
+
+//function prototypes
+
+/**
+ * This function initializes all the boilerplate items for the OCF server.
+ * it relies strictly on the "C" layer of the OCF stack. This is common
+ * code for all plugins.
+ *
+ * @param[in] ctx The context structure used by this implementation
+ * @param[in] deviceName Name of the device as defined by the plugin
+ * @param[in] resourceType Resource type as defined by the plugin
+ *
+ * @returns MPM_RESULT_OK if no errors, MPM_RESULT_INTERNAL_ERROR if initialization failure
+ */
+static MPMResult initInfrastructure(MPMCommonPluginCtx *ctx, const char *deviceName,
+ const char *resourceType);
+
+/**
+ * this function is a blocking function that holds the iotivity processing
+ * loop. this OCF server relies strictly on the "C" layer of the OCF stack.
+ * This is common code for all plugins.
+ *
+ * @param[in,out] result Child process status
+ * @param[in] ctx The context structure used by this implementation
+ *
+ * @return MPM_RESULT_OK if no errors, MPM_RESULT_INTERNAL_ERROR if maintenance failure
+ */
+static MPMResult maintainInfrastructure(MPMResult result, MPMCommonPluginCtx *ctx);
+
+const char *g_date_of_manufacture = NULL;
+const char *g_firmware_version = NULL;
+const char *g_manufacturer_name = "Intel";
+const char *g_operating_system_version = NULL;
+const char *g_hardware_version = NULL;
+const char *g_platform_id = "ce530bf4-40ab-11e6-9cca-ff46602aca08";
+const char *g_manufacturer_url = NULL;
+const char *g_model_number = NULL;
+const char *g_platform_version = NULL;
+const char *g_support_url = NULL;
+const char *g_version = NULL;
+const char *g_system_time = NULL;
+
+static pthread_t processMessageFromPipeThread;
+
+/* plugin specific context storage point. the plugin owns the allocation
+ * and freeing of its own context
+ */
+MPMPluginCtx *g_plugin_context = NULL;
+
+
+/* Secure Virtual Resource database for Iotivity Server
+ * It contains Server's Identity and the PSK credentials
+ * of other devices which the server trusts
+ */
+static char CRED_FILE[] = "./oic_svr_db_server.json";
+extern MPMCommonPluginCtx *g_com_ctx;
+
+FILE *serverFOpen(const char *path, const char *mode)
+{
+ if (0 == strcmp(path, OC_SECURITY_DB_DAT_FILE_NAME))
+ {
+ return fopen(CRED_FILE, mode);
+ }
+ else
+ {
+ return fopen(path, mode);
+ }
+}
+
+std::unique_ptr<ConcurrentIotivityUtils> iotivityUtils = NULL;
+
+/**
+ * This is a non blocking pipe read function
+ *
+ * @param[in] fd file descriptor from where messages are to be read
+ * @param[in] com_ctx common context
+ * @param[in] ctx plugin specific context
+ *
+ * @return false if STOP request has come from the MPM, true if STOP request
+ * has not come from the MPM
+ */
+bool processMessagesFromMPM(int fd, MPMCommonPluginCtx *com_ctx, MPMPluginCtx *ctx)
+{
+ struct timeval tv;
+ fd_set fdset;
+ int nfd = -1;
+ ssize_t nbytes = 0;
+ bool shutdown = false;
+ MPMPipeMessage pipe_message;
+ g_com_ctx = com_ctx;
+
+ tv.tv_sec = 15;
+ tv.tv_usec = 0;
+
+ pipe_message.payloadSize = 0;
+ pipe_message.msgType = MPM_NOMSG;
+ pipe_message.payload = NULL;
+
+ FD_ZERO(&(fdset));
+ FD_SET(fd, &(fdset));
+ nfd = select(fd + 1, &(fdset), NULL, NULL, &tv);
+ if (nfd == -1)
+ {
+ OIC_LOG_V(ERROR, TAG, "select error: %s", strerror(errno));
+ }
+ else
+ {
+ if (FD_ISSET(fd, &(fdset)))
+ {
+ nbytes = MPMReadPipeMessage(fd, &pipe_message);
+ if (nbytes == 0)
+ {
+ OIC_LOG(DEBUG, TAG, "EOF was read and file descriptor was found to be closed");
+ shutdown = true;
+ }
+ if (nbytes > 0)
+ {
+ if (pipe_message.msgType == MPM_STOP)
+ {
+ shutdown = true;
+ }
+ else
+ {
+ MPMRequestHandler(&pipe_message, ctx);
+ }
+ }
+
+ OICFree((void*)pipe_message.payload);
+
+ }
+ }
+ return (shutdown);
+}
+
+MPMResult MPMPluginService(MPMCommonPluginCtx *ctx)
+{
+ MPMResult result = MPM_RESULT_INTERNAL_ERROR;
+ if (ctx == NULL)
+ {
+ OIC_LOG(ERROR, TAG, "Plugin context is NULL");
+ goto HandleError;
+ }
+
+ // plugin create is in the individual plugin.
+ result = pluginCreate(&g_plugin_context);
+
+ if (result != MPM_RESULT_OK || g_plugin_context == NULL)
+ {
+ OIC_LOG_V(ERROR, TAG, "Creation failed result: %d", result);
+ goto HandleError;
+ }
+
+ // initialize the OCF infrastructure to be setup for a server
+ result = initInfrastructure(ctx, g_plugin_context->device_name, g_plugin_context->resource_type);
+
+ if (result != MPM_RESULT_OK)
+ {
+ OIC_LOG_V(ERROR, TAG, "Error (%d) initializing OCF infrastructure", result);
+ goto HandleError;
+ }
+ if (ctx->reconnect_file_name != NULL)
+ {
+ strncpy(g_plugin_context->reconnect_file_name, ctx->reconnect_file_name,
+ strlen(ctx->reconnect_file_name));
+ }
+ else
+ {
+ memset(g_plugin_context->reconnect_file_name, 0, MPM_MAX_FILE_NAME_LENGTH);
+ }
+ // plugin start is in the individual plugin.
+ result = pluginStart(g_plugin_context);
+
+ if (result != MPM_RESULT_OK)
+ {
+ OIC_LOG_V(ERROR, TAG, "Failed to start %s plugin result: %d", g_plugin_context->device_name,
+ result);
+ }
+
+HandleError :
+ /* Sends the status of the child to Parent and
+ * in the case of success it act as blocking call.
+ */
+ result = maintainInfrastructure(result, ctx);
+
+ return (result);
+}
+
+static MPMResult setPlatformInfoParams(OCPlatformInfo &platform_info)
+{
+ if ((strlen(g_manufacturer_name) > MAX_MANUFACTURER_NAME_LENGTH))
+ {
+ OIC_LOG(ERROR, TAG, "Manufacture name string length exceeded max length");
+ return MPM_RESULT_INTERNAL_ERROR;
+ }
+
+ if (g_manufacturer_url != NULL && (strlen(g_manufacturer_url) > MAX_MANUFACTURER_URL_LENGTH))
+ {
+ OIC_LOG(ERROR, TAG, "Url string length exceeded max length");
+ return MPM_RESULT_INTERNAL_ERROR;
+ }
+
+ platform_info.platformID = const_cast<char *> (g_platform_id);
+ platform_info.manufacturerName = const_cast<char *> (g_manufacturer_name);
+ platform_info.manufacturerUrl = const_cast<char *> (g_manufacturer_url);
+ platform_info.modelNumber = const_cast<char *> (g_model_number);
+ platform_info.dateOfManufacture = const_cast<char *> (g_date_of_manufacture);
+ platform_info.platformVersion = const_cast<char *> (g_platform_version);
+ platform_info.operatingSystemVersion = const_cast<char *> (g_operating_system_version);
+ platform_info.hardwareVersion = const_cast<char *> (g_hardware_version);
+ platform_info.firmwareVersion = const_cast<char *> (g_firmware_version);
+ platform_info.supportUrl = const_cast<char *> (g_support_url);
+ platform_info.systemTime = const_cast<char *> (g_system_time);
+
+ return MPM_RESULT_OK;
+}
+
+static MPMResult setDeviceInfoParams(const char *deviceName, const char *resourceType,
+ OCDeviceInfo &device_info)
+{
+ if (!deviceName || deviceName[0] == '\0')
+ {
+ OIC_LOG(ERROR, TAG, "Device name is NULL");
+ return MPM_RESULT_INVALID_PARAMETER;
+ }
+ device_info.deviceName = const_cast<char *> (deviceName);
+
+ OCStringLL *stringll = NULL;
+
+ OCResourcePayloadAddStringLL(&stringll, OC_RSRVD_RESOURCE_TYPE_DEVICE);
+ OCResourcePayloadAddStringLL(&stringll, resourceType);
+
+ device_info.types = stringll;
+
+ device_info.dataModelVersions = NULL;
+ device_info.specVersion = NULL;
+
+ return MPM_RESULT_OK;
+}
+
+static MPMResult initInfrastructure(MPMCommonPluginCtx *ctx, const char *deviceName,
+ const char *resourceType)
+{
+ MPMResult result = MPM_RESULT_INTERNAL_ERROR;
+
+ uint16_t port = 0;
+
+ if (ctx != NULL)
+ {
+ /* Create the unnamed pipe listener thread that will clear the
+ * child's stay in the loop variable when the parent wants to
+ * shut down the child process.
+ */
+ // Initialize Persistent Storage for SVR database
+ static OCPersistentStorage ps = {g_plugin_context->open, fread, fwrite, fclose, unlink};
+ OCRegisterPersistentStorageHandler(&ps);
+
+ OCStackResult ocResult = OCInit(NULL, port, OC_SERVER);
+ if (ocResult != OC_STACK_OK)
+ {
+ OIC_LOG(ERROR, TAG, "OCStack init error");
+ return result;
+ }
+
+ std::unique_ptr<WorkQueue<std::unique_ptr<IotivityWorkItem>>> q =
+ std::unique_ptr<WorkQueue<std::unique_ptr<IotivityWorkItem>>>
+ (new WorkQueue<std::unique_ptr<IotivityWorkItem>>());
+
+ iotivityUtils = make_unique<ConcurrentIotivityUtils>(std::move(q));
+ iotivityUtils->startWorkerThreads();
+
+ OCPlatformInfo platform_info;
+ if (setPlatformInfoParams(platform_info) != MPM_RESULT_OK)
+ {
+ OIC_LOG(ERROR, TAG, "Platform info setting failed locally!");
+ return result;
+ }
+
+ if (OCSetPlatformInfo(platform_info) != OC_STACK_OK)
+ {
+ OIC_LOG(ERROR, TAG, "Platform Registration failed!");
+ return result;
+ }
+
+ OCDeviceInfo device_info = {NULL, NULL, NULL, NULL};
+ if (setDeviceInfoParams(deviceName, resourceType, device_info) != MPM_RESULT_OK)
+ {
+ OIC_LOG(ERROR, TAG, "Device info setting failed locally!");
+ return result;
+ }
+
+ if (OCSetDeviceInfo(device_info) != OC_STACK_OK)
+ {
+ OIC_LOG(ERROR, TAG, "Device Registration failed!");
+ return result;
+ }
+ // Iotivity does not take ownership of 'types' and clones itself a copy.
+ OCFreeOCStringLL(device_info.types);
+ }
+ return MPM_RESULT_OK;
+}
+
+void *processMessageFromPipeThreadProc(void *arg)
+{
+ MPMCommonPluginCtx *ctx = (MPMCommonPluginCtx *)arg;
+ while (true)
+ {
+ bool result = processMessagesFromMPM(ctx->child_reads_fds.read_fd, ctx, g_plugin_context);
+ if (result != MPM_RESULT_OK)
+ {
+ OIC_LOG(INFO, TAG, "Leaving processMessageFromPipeThreadProc ");
+ break;
+ }
+ }
+ pthread_exit(NULL);
+ return NULL;
+}
+
+MPMResult maintainInfrastructure(MPMResult result, MPMCommonPluginCtx *ctx)
+{
+ MPMPipeMessage pipe_message;
+ void *res;
+
+ if (ctx != NULL)
+ {
+ /* child tells status to parent prior to getting in the
+ * processing loop
+ */
+ if (result == MPM_RESULT_OK)
+ {
+ pipe_message.msgType = MPM_DONE;
+ pipe_message.payloadSize = 0;
+ pipe_message.payload = NULL;
+ result = MPMWritePipeMessage(ctx->parent_reads_fds.write_fd, &pipe_message);
+ }
+ else
+ {
+ pipe_message.msgType = MPM_ERROR;
+ pipe_message.payloadSize = 0;
+ pipe_message.payload = NULL;
+ result = MPMWritePipeMessage(ctx->parent_reads_fds.write_fd, &pipe_message);
+ }
+
+ if (result == MPM_RESULT_OK)
+ {
+ pthread_create(&processMessageFromPipeThread, NULL, processMessageFromPipeThreadProc, ctx);
+ pthread_join(processMessageFromPipeThread, &res);
+ }
+ else
+ {
+ return result;
+ }
+
+ OIC_LOG_V(INFO, TAG, "Stopping %s plugin", g_plugin_context->device_name);
+
+ result = pluginStop(g_plugin_context);
+
+ if (result != MPM_RESULT_OK)
+ {
+ OIC_LOG_V(ERROR, TAG, "Error(%d) stopping plugin %s", result, g_plugin_context->device_name);
+ }
+
+ result = pluginDestroy(g_plugin_context);
+ if (result != MPM_RESULT_OK)
+ {
+ OIC_LOG_V(ERROR, TAG, "Error(%d) stopping plugin %s", result, g_plugin_context->device_name);
+ }
+
+ iotivityUtils->stopWorkerThreads();
+
+ OCStackResult ocResult = OCStop();
+
+ if (ocResult != OC_STACK_OK)
+ {
+ OIC_LOG_V(ERROR, TAG, "Error(%d) stopping Iotivity", ocResult);
+ result = MPM_RESULT_INTERNAL_ERROR;
+ }
+ }
+ else
+ {
+ OIC_LOG(ERROR, TAG, "Bad context handed to maintain ocf infrastructure");
+ }
+ return (result);
+}
+
+MPMResult MPMExtractFiltersFromQuery(char *query, char **filterOne, char **filterTwo)
+{
+
+ char *key = NULL;
+ char *value = NULL;
+ char *restOfQuery = NULL;
+ int numKeyValuePairsParsed = 0;
+
+ *filterOne = NULL;
+ *filterTwo = NULL;
+
+ if (!query)
+ {
+ // This is fine. This call is stemming from a non-entity handler request.
+ return MPM_RESULT_OK;
+ }
+
+ char *keyValuePair = strtok_r(query, OC_QUERY_SEPARATOR, &restOfQuery);
+
+ while (keyValuePair)
+ {
+ if (numKeyValuePairsParsed >= 2)
+ {
+ OIC_LOG(ERROR, TAG, "More than 2 queries params in URI.");
+ return MPM_RESULT_INVALID_PARAMETER;
+ }
+
+ key = strtok_r(keyValuePair, OC_KEY_VALUE_DELIMITER, &value);
+
+ if (!key || !value)
+ {
+ return MPM_RESULT_INVALID_PARAMETER;
+ }
+ else if (strncasecmp(key, OC_RSRVD_INTERFACE, sizeof(OC_RSRVD_INTERFACE) - 1) == 0)
+ {
+ *filterOne = value; // if
+ }
+ else if (strncasecmp(key, OC_RSRVD_RESOURCE_TYPE, sizeof(OC_RSRVD_INTERFACE) - 1) == 0)
+ {
+ *filterTwo = value; // rt
+ }
+ else
+ {
+ OIC_LOG_V(ERROR, TAG, "Unsupported query key: %s", key);
+ return MPM_RESULT_INVALID_PARAMETER;
+ }
+ ++numKeyValuePairsParsed;
+
+ keyValuePair = strtok_r(NULL, OC_QUERY_SEPARATOR, &restOfQuery);
+ }
+ if (*filterOne != NULL || *filterTwo != NULL)
+ {
+ OIC_LOG_V(ERROR, TAG, "Extracted params if: %s and rt: %s.", *filterOne, *filterTwo);
+ }
+
+ return MPM_RESULT_OK;
+}
--- /dev/null
+//******************************************************************
+//
+// Copyright 2017 Intel Mobile Communications GmbH All Rights Reserved.
+//
+//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+//
+// 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.
+//
+//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+//
+
+
+#ifndef _CONCURRENTIOTIVITYUTILS_H_
+#define _CONCURRENTIOTIVITYUTILS_H_
+
+#include <queue>
+#include <mutex>
+#include <memory>
+#include <condition_variable>
+#include <thread>
+#include <unistd.h>
+#include <iostream>
+#include <string>
+#include <memory>
+#include <map>
+#include "IotivityWorkItem.h"
+#include "WorkQueue.h"
+#include "ocstack.h"
+#include "octypes.h"
+
+namespace OC
+{
+ namespace Bridging
+ {
+ template<typename T, typename... Args>
+ std::unique_ptr<T> make_unique(Args &&... args)
+ {
+ return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
+ }
+
+ /**
+ * Provides a synchronized C++ wrapper over the Iotivity CSDK.
+ * Accepts workItems from the plugins for common operations.
+ * A consumer thread processes these worker items and makes calls into Iotivity.
+ * Another thread calls OCProcess() to check for network requests.
+ */
+ class ConcurrentIotivityUtils
+ {
+ private:
+
+ static std::unique_ptr<WorkQueue<std::unique_ptr<IotivityWorkItem>>> m_queue;
+ std::mutex m_iotivityApiCallMutex;
+
+ std::thread m_processWorkQueueThread, m_ocProcessThread;
+ bool m_threadStarted;
+ bool m_shutDownOCProcessThread;
+ static const int OCPROCESS_SLEEP_MICROSECONDS = 200000;
+
+ // Fetches work item from queue and processes it.
+ void processWorkQueue()
+ {
+ while (true)
+ {
+ std::unique_ptr<IotivityWorkItem> workItem;
+ bool fetchedWorkItem = m_queue->get(&workItem);
+
+ if (fetchedWorkItem)
+ {
+ std::lock_guard<std::mutex> lock(m_iotivityApiCallMutex);
+ workItem->process();
+ }
+ else
+ {
+ break;
+ }
+ }
+ }
+
+ void callOCProcess()
+ {
+ while (!m_shutDownOCProcessThread)
+ {
+ {
+ std::lock_guard<std::mutex> lock(m_iotivityApiCallMutex);
+ OCProcess();
+ }
+ // Hopefully it's enough for other threads to be scheduled
+ // instead of the spin here. OCProcess is very lightweight though.
+ usleep(OCPROCESS_SLEEP_MICROSECONDS);
+ }
+ }
+
+ public:
+
+
+ ConcurrentIotivityUtils(std::unique_ptr<WorkQueue<std::unique_ptr<IotivityWorkItem>>>
+ queueToMonitor)
+ {
+ m_queue = std::move(queueToMonitor);
+ m_threadStarted = false;
+ m_shutDownOCProcessThread = false;
+ }
+
+ /**
+ * Starts 2 worker threads. One to service the concurrent work queue to call
+ * into Iotivity. One to process network requests by calling OCProcess()
+ */
+ void startWorkerThreads();
+
+ /**
+ * Stops the 2 worker threads started by startWorkerThreads. @see startWorkerThreads
+ */
+ void stopWorkerThreads();
+
+ /**
+ * Gets the string uri associated with an Iotivity handle.
+ * @warning This function is not thread safe and should only be called from entityHandler
+ * specified when creating the resource. The entityhandler is called with the
+ * Iotivity access mutex locked and this function does not modify anything
+ * in the stack.
+ *
+ * @param[in] handle handle for the resource
+ * @param[out] uri uri associated with the handle
+ *
+ * @return true if the resource is found and uri will be populated, else false.
+ */
+ bool static getUriFromHandle(OCResourceHandle handle, std::string &uri);
+
+ /**
+ * Sends out OBSERVE notifications for the resource with the given uri.
+ * Notifications are sent out using OC_NA_QOS.
+ *
+ * @param[in] resourceUri resource uri for fetching handle and notifying
+ *
+ * @return OCStackResult OC_STACK_OK on success, some other value upon failure.
+ */
+ OCStackResult static queueNotifyObservers(const std::string &resourceUri);
+
+ /**
+ * Create an Iotivity resource with the given properties.
+ *
+ * @param[in] uri
+ * @param[in] resourceType
+ * @param[in] interface
+ * @param[in] entityHandler Callback function that will be called on requests to this resource.
+ * @param[in] callbackParam
+ * @param[in] resourceProperties
+ *
+ * @return OCStackResult OC_STACK_OK on success, some other value upon failure.
+ */
+ OCStackResult static
+ queueCreateResource(const std::string &uri, const std::string &resourceType,
+ const std::string &interface,
+ OCEntityHandler entityHandler, void *callbackParam,
+ uint8_t resourceProperties);
+
+ /**
+ * Delete the Iotivity resource given in the uri.
+ *
+ * @param[in] uri
+ *
+ * @return OCStackResult OC_STACK_OK on success, some other value upon failure.
+ */
+ OCStackResult static queueDeleteResource(const std::string &uri);
+
+ /**
+ * Send a response to a request.
+ *
+ * @param[in] request OCEntityHandleRequest type that was handed in the entityhandler.
+ * @param[in] payload The response payload. This is cloned and callee still has ownership
+ * and the onus to free this.
+ * @param[in] responseCode The response code of type OCEntityHandlerResult in ocstack.h
+ *
+ * @return OCStackResult OC_STACK_OK on success, some other value upon failure.
+ */
+ OCStackResult static
+ respondToRequest(OCEntityHandlerRequest *request, OCRepPayload *payload,
+ OCEntityHandlerResult responseCode);
+
+ /**
+ * Respond with an error message. Internally calls
+ * ConcurrentIotivityUtils::respondToRequest() after creating
+ * a payload containing the error message. The error message
+ * key will be x.org.iotivity.error and the value will be
+ * errorMessage.
+ *
+ * @param[in] request EntityHandler request
+ * @param[in] errorMessage May be NULL.
+ * @param[in] errorCode entity handler result
+ *
+ * @return OCStackResult OC_STACK_OK on success, some other value upon failure.
+ */
+ OCStackResult static respondToRequestWithError(OCEntityHandlerRequest *request,
+ const std::string &errorMessage,
+ OCEntityHandlerResult errorCode);
+
+ /**
+ * Parse the query parameter as a keyValueMap
+ *
+ * @param[in] query query to be parsed
+ * @param[in,out] keyValueMap key value map of the query
+ */
+ void static getKeyValueParams(const std::string &query,
+ std::map<std::string, std::string> &keyValueMap);
+
+ /**
+ * Can be called from the entity handler to handle requests for default interface
+ *
+ * @param[in] query
+ *
+ * @return true if request for default interface (oic.if.baseline) else false.
+ */
+ bool static isRequestForDefaultInterface(const std::string &query);
+
+ };
+ }
+}
+
+#endif //_CONCURRENTIOTIVITYUTILS_H_
--- /dev/null
+//******************************************************************
+//
+// Copyright 2017 Intel Mobile Communications GmbH All Rights Reserved.
+//
+//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+//
+// 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.
+//
+//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+//
+
+#ifndef _IOTIVITYWORKITEM_H_
+#define _IOTIVITYWORKITEM_H_
+
+#include "ocstack.h"
+#include "octypes.h"
+#include "ocpayload.h"
+#include "logger.h"
+#include <string>
+
+#define LOG "IOTIVITY_WORK_ITEM"
+
+namespace OC
+{
+ namespace Bridging
+ {
+ /**
+ * Basic workitem to act as a base class so more specific
+ * child classes can populate fields and
+ */
+ class IotivityWorkItem
+ {
+ public:
+ virtual void process() = 0;
+ virtual ~IotivityWorkItem() {};
+
+ protected:
+ std::string m_uri;
+ };
+
+ /**
+ * Creates an object used to create Iotivity resources.
+ */
+ class CreateResourceItem : public IotivityWorkItem
+ {
+ public:
+ CreateResourceItem(
+ std::string uri,
+ std::string resourceType,
+ std::string interface,
+ OCEntityHandler entityHandler,
+ void *callbackParam,
+ uint8_t resourceProperties
+ )
+ : m_resourceType(resourceType)
+ , m_interface(interface)
+ , m_entityHandler(entityHandler)
+ , m_callbackParam(callbackParam)
+ , m_resourceProperties(resourceProperties)
+ {
+ m_uri = uri;
+ }
+
+ virtual void process()
+ {
+ OCResourceHandle handle;
+
+ OCStackResult res = OCCreateResource(&handle, m_resourceType.c_str(),
+ m_interface.c_str(),
+ m_uri.c_str(),
+ m_entityHandler,
+ m_callbackParam,
+ m_resourceProperties);
+
+ if (res == OC_STACK_OK)
+ {
+ OIC_LOG_V(INFO, LOG, "Created and saved %s", m_uri.c_str());
+ }
+ else
+ {
+ OIC_LOG_V(ERROR, LOG, "Failed to create %s", m_uri.c_str());
+ }
+ }
+ private:
+ std::string m_resourceType;
+ std::string m_interface;
+ OCEntityHandler m_entityHandler;
+ void *m_callbackParam;
+ uint8_t m_resourceProperties;
+ };
+
+ /**
+ * Creates an object used to create send Iotivity responses.
+ */
+ class SendResponseItem : public IotivityWorkItem
+ {
+ public:
+ SendResponseItem(std::unique_ptr<OCEntityHandlerResponse> response)
+ : m_response(std::move(response))
+ {}
+
+ virtual void process()
+ {
+ OCDoResponse((m_response).get());
+ OCPayloadDestroy(m_response->payload);
+ }
+
+ private:
+ std::unique_ptr<OCEntityHandlerResponse> m_response;
+ };
+
+ /**
+ * Creates an object used to notify observers of Iotivity resources.
+ */
+ class NotifyObserversItem : public IotivityWorkItem
+ {
+ public:
+ NotifyObserversItem(const std::string &uri)
+ {
+ m_uri = uri;
+ }
+
+ virtual void process()
+ {
+ OCResourceHandle handle = OCGetResourceHandleAtUri(m_uri.c_str());
+
+ if (!handle)
+ {
+ OIC_LOG_V(ERROR, LOG, "No handle for %s. Not notifying observers.", m_uri.c_str());
+ return;
+ }
+ OCNotifyAllObservers(handle, OC_NA_QOS);
+ }
+
+ };
+
+ /**
+ * Creates an object used to delete Iotivity resources.
+ */
+ class DeleteResourceItem : public IotivityWorkItem
+ {
+ public:
+ DeleteResourceItem(const std::string &uri)
+ {
+ m_uri = uri;
+ }
+
+ virtual void process()
+ {
+ OCResourceHandle handle = OCGetResourceHandleAtUri(m_uri.c_str());
+
+ if (!handle)
+ {
+ OIC_LOG_V(ERROR, LOG, "No handle for %s. Nothing to delete", m_uri.c_str());
+ return;
+ }
+
+ OCStackResult res = OCDeleteResource(handle);
+ if (res == OC_STACK_OK)
+ {
+ OIC_LOG_V(INFO, LOG, "Deleted %s", m_uri.c_str());
+ }
+ else
+ {
+ OIC_LOG_V(ERROR, LOG, "Failed to delete %s", m_uri.c_str());
+ }
+ }
+
+ };
+ }
+}
+#endif // _IOTIVITYWORKITEM_H_
--- /dev/null
+//******************************************************************
+//
+// Copyright 2017 Intel Mobile Communications GmbH All Rights Reserved.
+//
+//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+//
+// 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.
+//
+//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+//
+
+
+#ifndef __JSONHELPER_H__
+#define __JSONHELPER_H__
+
+#include "rapidjson.h"
+#include "document.h"
+#include "stringbuffer.h"
+#include "writer.h"
+#include <string>
+
+#include <typeinfo>
+
+class JsonHelper
+{
+ public:
+
+ static std::string toString(rapidjson::Value &doc)
+ {
+ rapidjson::StringBuffer sb;
+ std::string jsonRep;
+
+ rapidjson::Writer<rapidjson::StringBuffer> writer(sb);
+ doc.Accept(writer);
+ return sb.GetString();
+ }
+
+ static std::string toString(rapidjson::Value::ConstMemberIterator &it)
+ {
+ rapidjson::StringBuffer sb;
+ std::string jsonRep;
+
+ rapidjson::Writer<rapidjson::StringBuffer> writer(sb);
+ it->value.Accept(writer);
+ return sb.GetString();
+ }
+
+ template<typename T>
+ static void setMember(rapidjson::Document &doc, std::string obj,
+ std::string name, T &value)
+ {
+ if (doc.HasMember(obj.c_str()))
+ {
+ rapidjson::Document::AllocatorType &allocator = doc.GetAllocator();
+ doc[obj.c_str()].RemoveMember(name.c_str());
+ rapidjson::Value nameValue(name.c_str(), allocator);
+ doc[obj.c_str()].AddMember(nameValue, value, allocator);
+ }
+ }
+
+ template<typename T>
+ static void setMember(rapidjson::Document &doc, const std::string &name, T &value)
+ {
+ if (doc.HasMember(name.c_str()))
+ {
+ doc[name.c_str()] = value;
+ }
+ else
+ {
+ rapidjson::Document::AllocatorType &allocator = doc.GetAllocator();
+ rapidjson::Value nameValue(name.c_str(), allocator);
+ doc.AddMember(nameValue, value, allocator);
+ }
+
+ }
+
+ template<typename T>
+ static bool getMember(rapidjson::Document &doc, std::string name, T &value)
+ {
+ if (doc.HasMember(name.c_str()))
+ {
+ if (typeid(T) == typeid(std::string))
+ {
+ value = doc[name.c_str()].GetString();
+ }
+ return true;
+ }
+ return false;
+ }
+
+ static void tryRemoveMember(rapidjson::Document &doc, std::string name)
+ {
+ if (doc.HasMember(name.c_str()))
+ {
+ doc.RemoveMember(name.c_str());
+ }
+ }
+};
+
+#endif /* __JSON_HELPER_H__ */
--- /dev/null
+//******************************************************************
+//
+// Copyright 2017 Intel Mobile Communications GmbH All Rights Reserved.
+//
+//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+//
+// 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.
+//
+//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+//
+
+#ifndef _WORKQUEUE_H_
+#define _WORKQUEUE_H_
+
+#include <queue>
+#include <mutex>
+#include <memory>
+#include <condition_variable>
+#include <thread>
+#include "iotivity_config.h"
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+namespace OC
+{
+ namespace Bridging
+ {
+ /**
+ * Provides a generic, minimal thread safe queue.
+ */
+ template<class T>
+ class WorkQueue
+ {
+ private:
+
+ std::queue<T> m_workQueue;
+ std::mutex m_workQueueMutex;
+ std::condition_variable m_cv;
+ bool m_signalToShutDown;
+
+ public:
+
+ inline WorkQueue()
+ {
+ m_signalToShutDown = false;
+ }
+
+ /**
+ * Puts the arg in the queue and notifies all threads waiting
+ * to fetch things from the queue.
+ *
+ * @para[in] m item The item to insert into the queue.
+ */
+ void put(T item)
+ {
+ std::unique_lock<std::mutex> lock(m_workQueueMutex);
+
+ m_workQueue.push(std::move(item));
+ m_cv.notify_all();
+ }
+
+ /**
+ * Blocking function to fetch an item from the queue.
+ *
+ * @param[in] item The item to insert into the queue.
+ * @return true if an item is fetched from the queue.
+ false if the queue is shutdown.
+ */
+ bool get(T *item)
+ {
+ std::unique_lock<std::mutex> lock(m_workQueueMutex);
+
+ m_cv.wait(lock, [this]()
+ {
+ return m_workQueue.size() > 0 || m_signalToShutDown;
+ });
+
+ if (m_signalToShutDown)
+ {
+ return false;
+ }
+
+ *item = std::move(m_workQueue.front());
+ m_workQueue.pop();
+ return true;
+ }
+
+ /**
+ * Notifies all waiting threads that the queue is being shut down.
+ */
+ void shutdown()
+ {
+ std::unique_lock<std::mutex> lock(m_workQueueMutex);
+ m_signalToShutDown = true;
+ m_cv.notify_all();
+ }
+ };
+ }
+}
+
+#endif // _WORKQUEUE_H_
--- /dev/null
+//******************************************************************
+//
+// Copyright 2017 Intel Mobile Communications GmbH All Rights Reserved.
+//
+//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+//
+// 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.
+//
+//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+//
+
+#ifndef _CURLCLIENT_H_
+#define _CURLCLIENT_H_
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <memory.h>
+#include <string>
+#include <vector>
+#include <map>
+#include <curl/curl.h>
+#include <stdexcept>
+#include "mpmErrorCode.h"
+#include "StringConstants.h"
+
+namespace OC
+{
+ namespace Bridging
+ {
+ const char CURL_CONTENT_TYPE_JSON[] = "content-type: application/json";
+ const char CURL_CONTENT_TYPE_URL_ENCODED[] = "content-type: application/x-www-form-urlencoded";
+ const char CURL_HEADER_ACCEPT_JSON[] = "accept: application/json";
+
+ const long INVALID_RESPONSE_CODE = 0;
+
+ class CurlClient
+ {
+
+ public:
+ enum class CurlMethod
+ {
+ GET, PUT, POST, DELETE, HEAD
+ };
+
+ virtual ~CurlClient() { }
+
+ long getLastResponseCode()
+ {
+ return m_lastResponseCode;
+ }
+
+ CurlClient(CurlMethod method, const std::string &url)
+ {
+ if (url.empty())
+ {
+ throw "Curl method or url is empty";
+ }
+
+ m_method = getCurlMethodString(method);
+ m_url = url;
+ m_useSsl = CURLUSESSL_TRY;
+ }
+
+ CurlClient &setRequestHeaders(std::vector<std::string> &requestHeaders)
+ {
+ m_requestHeaders = requestHeaders;
+ return *this;
+ }
+
+ CurlClient &addRequestHeader(const std::string &header)
+ {
+ m_requestHeaders.push_back(header);
+ return *this;
+ }
+
+ CurlClient &setUserName(const std::string &userName)
+ {
+ m_username = userName;
+ return *this;
+ }
+
+ CurlClient &setRequestBody(std::string &requestBody)
+ {
+ m_requestBody = requestBody;
+ return *this;
+ }
+
+ CurlClient &setUseSSLOption(curl_usessl sslOption)
+ {
+ m_useSsl = sslOption;
+ return *this;
+ }
+
+ int send()
+ {
+ return doInternalRequest(m_url, m_method, m_requestHeaders, m_requestBody, m_username, m_outHeaders,
+ m_response);
+ }
+
+ std::string getResponseBody()
+ {
+ return m_response;
+ }
+
+ std::vector<std::string> getResponseHeaders()
+ {
+ return m_outHeaders;
+ }
+
+
+ private:
+
+ std::string getCurlMethodString(CurlMethod method)
+ {
+ if (method == CurlMethod::GET) return OC::PlatformCommands::GET;
+ else if (method == CurlMethod::PUT) return OC::PlatformCommands::PUT;
+ else if (method == CurlMethod::POST) return OC::PlatformCommands::POST;
+ else if (method == CurlMethod::DELETE) return OC::PlatformCommands::DELETE;
+ else if (method == CurlMethod::HEAD) return "HEAD";
+
+ else throw std::runtime_error("Invalid CurlMethod");
+ }
+
+ std::string m_url;
+ std::string m_method;
+ std::vector<std::string> m_requestHeaders;
+ std::string m_requestBody;
+ std::string m_username;
+ std::string m_response;
+ std::vector<std::string> m_outHeaders;
+
+ /// Indicates whether to use CURLOPT_USE_SSL option in doInternalRequest.
+ /// Curl default is no SSL (CURLUSESSL_NONE). Specify one of the other CURL SSL options
+ /// (for example, CURLUSESSL_TRY) if you need to perform SSL transactions.
+ curl_usessl m_useSsl;
+
+ static size_t WriteCallback(void *contents, size_t size, size_t nmemb, void *userp);
+
+ // Represents contiguous memory to hold a HTTP response.
+ typedef struct _MemoryChunk
+ {
+ _MemoryChunk() : size(0)
+ {
+ memory = static_cast<char *>(malloc(1));
+ }
+
+ char *memory;
+ size_t size;
+
+ } MemoryChunk;
+
+ int decomposeHeader(const char *header, std::vector<std::string> &headers);
+
+
+ int doInternalRequest(const std::string &url,
+ const std::string &method,
+ const std::vector<std::string> &inHeaders,
+ const std::string &request,
+ const std::string &username,
+ std::vector<std::string> &outHeaders,
+ std::string &response);
+
+ long m_lastResponseCode;
+ };
+ } // namespace Bridging
+} // namespace OC
+#endif // _CURLCLIENT_H_
--- /dev/null
+//******************************************************************
+//
+// Copyright 2017 Intel Mobile Communications GmbH All Rights Reserved.
+//
+//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+//
+// 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.
+//
+//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+//
+
+/* This file contains the plugin server implementation. Most of this
+ * implementation is reused for other plugins. There is NO customization
+ * required of functions in this file to accommodate plugin specific code.
+ */
+
+#ifndef _MESSAGEHANDLER_H
+#define _MESSAGEHANDLER_H
+
+#include <stdint.h>
+#include <stdio.h>
+#include "mpmErrorCode.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define MPM_MAX_URI_LEN 256
+#define MPM_MAX_FILE_NAME_LENGTH 300
+#define MPM_MAX_LENGTH_32 32
+#define MPM_MAX_LENGTH_64 64
+#define MPM_MAX_LENGTH_256 256
+#define MPM_MAX_UNIQUE_ID_LEN 128
+#define MPM_MAX_METADATA_LEN 3000
+
+/* Enum to specify the action type*/
+typedef enum
+{
+ MPM_NOMSG = 0,
+ MPM_SCAN,
+ MPM_ADD,
+ MPM_DELETE,
+ MPM_REMOVE,
+ MPM_RECONNECT,
+ MPM_STOP,
+ MPM_DONE,
+ MPM_ERROR
+} MPMMessageType;
+
+/**
+ * This structure represents the format of the message exchanged
+ * between MPM and Plugin over the pipe.
+ */
+typedef struct
+{
+ /** size of the payload. */
+ size_t payloadSize;
+
+ /** type of the message [MPM_SCAN, MPM_ADD etc] */
+ MPMMessageType msgType;
+
+ /** Payload to be sent and received */
+ const uint8_t *payload;
+} MPMPipeMessage;
+
+/**
+ * This structure represents the add response message coming from
+ * the plugins to mpm library
+ */
+typedef struct
+{
+ char uri[MPM_MAX_URI_LEN];
+ char metadata[MPM_MAX_METADATA_LEN];
+} MPMAddResponse;
+
+/**
+ * This structure represents the resource list used for
+ * creating reconnect metadata
+ */
+typedef struct MPMResourceList
+{
+ char href[MPM_MAX_URI_LEN];
+ char relative[MPM_MAX_LENGTH_64];
+ char interfaces[MPM_MAX_LENGTH_64];
+ char rt[MPM_MAX_LENGTH_64];
+ int bitmap;
+ struct MPMResourceList *next;
+} MPMResourceList;
+
+/**
+ * This structure represents the device specific data of the plugin
+ * which is a part of reconnect metadata
+ */
+typedef struct
+{
+ char devName[MPM_MAX_LENGTH_64];
+ char devType[MPM_MAX_LENGTH_64];
+ char manufacturerName[MPM_MAX_LENGTH_256];
+} MPMDeviceSpecificData;
+
+/**
+ * This function writes messages to the pipe
+ * @param[in] fd file descriptor
+ * @param[in] pipe_message message to be written
+ *
+ * @return MPM_RESULT_OK on success, MPM_RESULT_INTERNAL_PARAMETER on failure
+*/
+MPMResult MPMWritePipeMessage(int fd, const MPMPipeMessage *pipe_message);
+
+/**
+ * This function reads messages from the pipe
+ * @param[in] fd file descriptor
+ * @param[in,out] pipe_message for storing the read message.
+ *
+ * @return number of bytes read
+*/
+ssize_t MPMReadPipeMessage(int fd, MPMPipeMessage *pipe_message);
+
+
+/**
+ * This function encodes the metadata received from the plugin
+ * @param[in] list A list of resources supported by the device
+ * @param[in] deviceDetails Plugin specific details to be encoded
+ * @param[in] buff The metadata stream to be filled with encoded data
+ * @param[in] size Size of the metadata stream to be encoded
+ * @param[in] details To hold plugin specific device details to be encoded
+ * @param[in] payloadSize Size of the plugin specific device details
+ *
+ * @return zero on no error, non-zero on occurrence of some error
+ */
+int64_t MPMFormMetaData(MPMResourceList *list, MPMDeviceSpecificData *deviceDetails, uint8_t *buff,
+ size_t size, void *details, size_t payloadSize);
+
+/**
+ * This function decodes and parse the metadata received from
+ * the client as a part of reconnect request
+ * @param[in] buffer The encoded metadata stream
+ * @param[in] size Size of the encoded metadata stream
+ * @param[out] list Reference to location of resource details
+ * @param[out] details To hold plugin specific device details after parsing
+ */
+void MPMParseMetaData(const uint8_t *buffer, size_t size, MPMResourceList **list, void **details);
+
+/**
+ * This function sends response coming from the plugins to mpm library
+ * @param[in] response Response to be sent
+ * @param[in] size Size of the response.
+ * @param[in] type Type of response (scan response, add response etc)
+ * @return MPM_RESULT_OK on success else MPM_RESULT_INTERNAL_ERROR
+ */
+MPMResult MPMSendResponse(const void *response, size_t size, MPMMessageType type);
+
+#ifdef __cplusplus
+}
+#endif // #ifdef __cplusplus
+
+#endif
--- /dev/null
+//******************************************************************
+//
+// Copyright 2017 Intel Mobile Communications GmbH All Rights Reserved.
+//
+//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+//
+// 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.
+//
+//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+//
+
+#ifndef _MPMERRORCODE_H_
+#define _MPMERRORCODE_H_
+
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Definitions for error codes for the Bridging
+ */
+
+typedef enum
+{
+ MPM_CB_RESULT_KEEP = 1,
+ MPM_CB_RESULT_DELETE = 2,
+ MPM_CB_RESULT_OK = 3,
+ MPM_CB_RESULT_ERROR = 4
+} MPMCbResult;
+
+typedef enum
+{
+ MPM_RESULT_OK = 0,
+ MPM_RESULT_CREATED_FAILED, /*< Module has failed during create. */
+
+ MPM_RESULT_INVALID_HANDLE, /*< The handle is invalid. */
+ MPM_RESULT_INVALID_PARAMETER, /*< Parameters are invalid. */
+ MPM_RESULT_INTERNAL_ERROR, /*< Some unknown error occurred. */
+ MPM_RESULT_INVALID_VERSION, /*< Invalid plugin API version. */
+
+ MPM_RESULT_MISSING_API, /*< One or more expected API is missing in the callback. */
+ MPM_RESULT_NOT_IMPLEMENTED, /*< API function is not implemented. */
+ MPM_RESULT_OUT_OF_MEMORY, /*< Memory allocation failure. */
+ MPM_RESULT_ERROR_ADD_MESSAGE_QUEUE, /*< add message queue failure. */
+ MPM_RESULT_LOADLIBRARY_FAILED,
+ MPM_RESULT_INSUFFICIENT_BUFFER,
+ MPM_RESULT_DUPLICATE_API_CALL,
+ MPM_RESULT_FILE_NOT_OPEN,
+ MPM_RESULT_FILE_ALREADY_OPEN,
+ MPM_RESULT_FILE_NOT_CLOSED,
+ MPM_RESULT_NOT_STARTED, /*< Module has not been started. */
+ MPM_RESULT_STARTED_FAILED, /*< Module has failed during start. */
+ MPM_RESULT_ALREADY_STARTED, /*< Module already started. */
+ MPM_RESULT_NOT_STOPPED,
+ MPM_RESULT_ALREADY_CREATED, /*< Module already created. */
+ MPM_RESULT_NOT_AUTHORIZED, /*< Not authorized */
+ MPM_RESULT_NOT_PRESENT, /*< Not present or available */
+ MPM_RESULT_NETWORK_ERROR,
+ MPM_RESULT_JSON_ERROR,
+ MPM_RESULT_MEMORY_ERROR,
+ MPM_RESULT_INVALID_DATA,
+ MPM_RESULT_INDEX_OUT_OF_BOUNDS, /*< Specified an index too great for array. */
+ MPM_RESULT_UNEXPECTED_RESULT /*< Result code did not match expected response */
+} MPMResult;
+
+#ifdef __cplusplus
+}
+#endif // #ifdef __cpluscplus
+
+#endif /* _MPMERRORCODEH_ */
--- /dev/null
+//******************************************************************
+//
+// Copyright 2017 Intel Mobile Communications GmbH All Rights Reserved.
+//
+//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+//
+// 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.
+//
+//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+//
+
+/* This file contains the "C" plugin interface definition. This header file
+ * and its corresponding implementation file is intended to be shared among
+ * ALL plugins. Modification of this file is not necessary for the construction
+ * of a new plugin.
+ */
+
+#ifndef _PLUGINIF_H_
+#define _PLUGINIF_H_
+
+#include <stdint.h>
+#include <stdio.h>
+#include <pthread.h>
+#include <stdbool.h>
+#include "messageHandler.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * This is a clear way to store file descriptors for unnamed pipes. Each pipe
+ * has a read end and a write end.
+ */
+struct MPMPipe
+{
+ int read_fd;
+ int write_fd;
+};
+
+/**
+ * structure to represent the plugin context
+ * which is common between all the plugins.
+ */
+struct MPMCommonPluginCtx
+{
+ /**
+ * Unnamed pipes are used in this implementation of the common plugin code,
+ * however the unnamed pipes are NOT exposed outside of the common plugin code.
+ * One of the unnamed pipes is used by the child to tell parent that the
+ * child has created and started the plugin content section successfully or not.
+ * The other unnamed pipe is used by the parent to stop the child which in turn
+ * stops and destroys the plugin content. As you know the unnamed pipes may be
+ * only used with related processes (i.e. having the same parent). The pipes exist
+ * as long as their descriptors are open.
+ */
+ MPMPipe parent_reads_fds;
+ MPMPipe child_reads_fds;
+
+ /**
+ * The "started" variable is used by the parent process to not permit
+ * more than one fork to happen per instance of the plugin.
+ */
+ bool started;
+
+ /**
+ * The main thread in the child process needs to know when it should
+ * leave the OCF process loop.
+ */
+ bool exit_process_loop;
+ char reconnect_file_name[MPM_MAX_FILE_NAME_LENGTH];
+
+ /**
+ * Save the child's process handle to wait on it being signaled later
+ */
+ pid_t child_pid;
+
+};
+
+/** time out value */
+#define MPM_TIMEOUT_VAL_IN_SEC 60
+
+/**
+ * This function is a OCF server. The function does not return unless there is an
+ * error or this main thread was signaled by the parent process main thread.
+ * @param[in] plugin_ctx plugin specific context
+ *
+ * @return MPM_RESULT_OK if no errors, MPM_RESULT_INTERNAL_ERROR if error
+ */
+MPMResult MPMPluginService(MPMCommonPluginCtx *plugin_ctx);
+
+#ifdef __cplusplus
+}
+#endif // #ifdef __cplusplus
+
+#endif /* __PLUGIN_IF_H__ */
--- /dev/null
+//******************************************************************
+//
+// Copyright 2017 Intel Mobile Communications GmbH All Rights Reserved.
+//
+//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+//
+// 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.
+//
+//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+//
+
+/* This file contains the interface between the plugin's OCF server and the
+ * plugin specific implmentation
+ */
+
+#ifndef __PLUGINSERVER_H__
+#define __PLUGINSERVER_H__
+
+#include "messageHandler.h"
+#include "pluginIf.h"
+#include "mpmErrorCode.h"
+#include "ocstack.h"
+#include <stdbool.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define MPM_THREAD_PROCESS_SLEEPTIME 5
+
+/**
+ * Plugin context is owned by the plugin specific content provider (you).
+ */
+struct MPMPluginCtx
+{
+ /**
+ * This plugin specific content provider has chosen to use a worker thread
+ * to manage all the devices. Keeping track if the thread is started.
+ */
+ bool started;
+
+ /**
+ * The thread is staying around for the lifetime of the plugin instance.
+ * This state variable is required to tell the thread leave the thread
+ * function when that time comes.
+ */
+ bool stay_in_process_loop;
+
+ FILE *(*open)(const char *path, const char *mode);
+
+ /**
+ * The name of the plugin being started. This name must follow OCF specification
+ * for '/oic/d' device name.
+ */
+ const char *device_name;
+ char reconnect_file_name[MPM_MAX_FILE_NAME_LENGTH];
+
+ /**
+ * The resource type with device of the plugin being started. This name must follow OCF specification
+ * for /oic/d. Some examples would be 'oic.d.light' and 'oic.d.smartlock'.
+ */
+ const char *resource_type;
+
+ /**
+ * Save the thread handle so there is a possibility to wait on thread
+ * handles for synchronization purposes.
+ */
+ pthread_t thread_handle;
+};
+
+/**
+ * These functions are called by the plugin's own Iotivity server which is
+ * common code for all plugins. There is one Iotivity server per plugin. The APIs
+ * shown below permit the plugin to initialize and to allow the plugin specific
+ * code to call other Iotivity csdk APIs. The implementation of these functions is found
+ * in the plugin specific code.
+ *
+ * @param[in] plugin_specific_ctx Plugin specific context
+ * @param[in] message Message received from the client via mpm library
+ *
+ * @return MPM_RESULT_OK if no error, error specific value if any error occurs
+ */
+MPMResult pluginCreate(MPMPluginCtx **plugin_specific_ctx);
+
+MPMResult pluginStart(MPMPluginCtx *plugin_specific_ctx);
+
+MPMResult pluginStop(MPMPluginCtx *plugin_specific_ctx);
+
+MPMResult pluginDestroy(MPMPluginCtx *plugin_specific_ctx);
+
+MPMResult pluginScan(MPMPluginCtx *ctx, MPMPipeMessage *message);
+
+MPMResult pluginAdd(MPMPluginCtx *ctx, MPMPipeMessage *message);
+
+MPMResult pluginRemove(MPMPluginCtx *ctx, MPMPipeMessage *message);
+
+MPMResult pluginReconnect(MPMPluginCtx *ctx, MPMPipeMessage *message);
+
+/**
+ * This function is added for Zigbee and kept as empty function
+ * for all other plugins to avoid using ifdef(s)
+ */
+void MPMPluginSpecificProcess(void);
+
+/**
+ * This function handles the requests from mpm library to plugin
+ * @param[in] message The message to received over the pipe from the mpm library
+ * @param[in] ctx Context of the plugin to which the message has to be forwarded to
+ */
+void MPMRequestHandler(MPMPipeMessage *message, MPMPluginCtx *ctx);
+
+
+/**
+ * Stolen from: IOTIVITY/resource/csdk/stack/src/resource.c:ExtractFiltersFromQuery()
+ *
+ * Function will extract 0, 1 or 2 filters from query.
+ * More than 2 filters or unsupported filters will result in error.
+ * If both filters are of the same supported type, the 2nd one will be picked.
+ * Resource and device filters in the SAME query are NOT validated
+ * and resources will likely not clear filters.
+ *
+ * @param[in] query The query string from which filters are to be extracted
+ * @param[in,out] filterOne The first filter string
+ * @param[in,out] filterTwo The second filter string
+ *
+ * @return MPM_RESULT_OK if no error, MPM_RESULT_INVALID_PARAMETER if any error
+ */
+MPMResult MPMExtractFiltersFromQuery(char *query, char **filterOne, char **filterTwo);
+
+#ifdef __cplusplus
+}
+#endif // #ifdef __cplusplus
+
+#endif /* __PLUGIN_SERVER_H__ */
--- /dev/null
+#******************************************************************
+#
+# Copyright 2017 Intel Mobile Communications GmbH All Rights Reserved.
+#
+#-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+#
+# 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.
+#
+#-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+##
+# Mini Plugin Manager build script
+##
+
+import os.path
+
+Import('env')
+
+target_os = env.get('TARGET_OS')
+src_dir = env.get('SRC_DIR')
+bridging_path = os.path.join(src_dir, 'bridging')
+
+print "Reading MPM script"
+
+######################################################################
+# Build flags
+######################################################################
+env.PrependUnique(CPPPATH = [ os.path.join(src_dir, 'resource', 'c_common', 'oic_string', 'include'),
+ os.path.join(src_dir, 'extlibs', 'rapidjson', 'include', 'rapidjson')
+ ])
+env.AppendUnique(CPPPATH = [ os.path.join(bridging_path, 'include')
+ ])
+
+if target_os not in ['arduino', 'windows']:
+ env.AppendUnique(CPPDEFINES = ['WITH_POSIX'])
+ env.AppendUnique(CXXFLAGS = ['-std=c++0x', '-Wall', '-Wextra', '-Werror'])
+
+if target_os in ['darwin','ios']:
+ env.AppendUnique(CPPDEFINES = ['_DARWIN_C_SOURCE'])
+
+env.AppendUnique(RPATH = [env.get('BUILD_DIR')])
+env.AppendUnique(LIBPATH = [env.get('BUILD_DIR')])
+
+if env.get('LOGGING'):
+ env.AppendUnique(CPPDEFINES = ['TB_LOG'])
+
+
+env.PrependUnique(LIBS = ['mpmcommon'])
+#####################################################################
+# Source files and Target(s)
+######################################################################
+minipluginmanager_src = [
+ os.path.join(bridging_path, 'mini_plugin_manager', 'miniPluginManager.cpp'),
+ ]
+
+env.AppendUnique(MINIPLUGINMANAGER_SRC =minipluginmanager_src)
+
+print "Include path is %s" % env.get('CPPPATH')
+print "Files path is %s" % env.get('MINIPLUGINMANAGER_SRC')
+if target_os in ['android', 'tizen']:
+ mpmlib = env.SharedLibrary('minipluginmanager', env.get('MINIPLUGINMANAGER_SRC'))
+else:
+ mpmlib = env.StaticLibrary('minipluginmanager', env.get('MINIPLUGINMANAGER_SRC'))
+env.InstallTarget(mpmlib, 'minipluginmanager')
+env.UserInstallTargetLib(mpmlib, 'minipluginmanager')
+
--- /dev/null
+//******************************************************************
+//
+// Copyright 2017 Intel Mobile Communications GmbH All Rights Reserved.
+//
+//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+//
+// 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 <stdio.h>
+#include <dlfcn.h>
+#include "iotivity_config.h"
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <ctype.h>
+#include <string.h>
+#include <stdint.h>
+#include <signal.h>
+#include <pthread.h>
+#include <sys/wait.h>
+#include <iostream>
+#include <vector>
+
+#define TAG "MINI_PLUGIN_MANAGER"
+
+#include "oic_malloc.h"
+#include "pluginIf.h"
+#include "miniPluginManager.h"
+#include "messageHandler.h"
+#include "logger.h"
+
+pthread_t readResponsethreadhandle;
+
+bool exitResponseThread = false;
+
+/*******************************************************************************
+ * type defines and structure definitions go here
+ ******************************************************************************/
+
+typedef MPMCommonPluginCtx *(*create_t)();
+
+typedef int32_t (*start_t)(MPMCommonPluginCtx *ctx);
+
+typedef void (*stop_t)(MPMCommonPluginCtx *ctx);
+
+typedef void (*destroy_t)(MPMCommonPluginCtx *ctx);
+
+typedef struct plugin_runtime_tag
+{
+ create_t create;
+ start_t start;
+ stop_t stop;
+ destroy_t destroy;
+} MPMPluginRunTime;
+
+/**
+ * structure representing plugin context
+ */
+typedef struct plugin_context_tag
+{
+ /** function pointers to the plugin life cycle methods */
+ MPMPluginRunTime lifecycle;
+
+ /** plugin handle */
+ void *handle;
+
+ /** common plugin context */
+ MPMCommonPluginCtx *plugin_ctx;
+
+ /** plugin shared object name */
+ char shared_object_name[MAX_SHARED_OBJECT_NAME_LENGTH];
+
+ MPMCallback callbackClient;
+} MPMPluginContext;
+
+std::vector<MPMPluginContext> g_LoadedPlugins;
+
+/**
+ * This is MPM library API to start the thread for handling response
+ * messages from the plugins (child processes)
+ */
+
+void startReadResponseThread();
+
+/**
+ * This is MPM library API to terminate the thread for handling response
+ * messages from the plugins (child processes)
+ */
+void stopReadResponseThread();
+
+/**
+ * This function runs as a thread which is running to handle
+ * the response messages from the plugins(child processes)
+ */
+static void *readResponse(void *)
+{
+ int status = 0;
+ fd_set readfds;
+ struct timeval tv;
+
+ std::vector<MPMPluginContext>::iterator loadedPluginsItr;
+
+ std::vector<MPMPluginContext> *loadedPlugins = &g_LoadedPlugins;
+
+ tv.tv_sec = 5;
+ tv.tv_usec = 0;
+
+ while (true)
+ {
+ if (exitResponseThread == true)
+ {
+ OIC_LOG(DEBUG, TAG, "Exiting readResponse thread");
+ break;
+ }
+ int maxFd = -1;
+
+ FD_ZERO(&(readfds));
+
+ loadedPluginsItr = loadedPlugins->begin();
+
+ for ( ; loadedPluginsItr != loadedPlugins->end(); )
+ {
+ MPMCommonPluginCtx *ctx = (*loadedPluginsItr).plugin_ctx;
+ if (ctx->started)
+ {
+ FD_SET(ctx->parent_reads_fds.read_fd, &(readfds));
+ if (maxFd < ctx->parent_reads_fds.read_fd)
+ {
+ maxFd = ctx->parent_reads_fds.read_fd;
+ }
+ }
+ loadedPluginsItr++;
+ }
+
+ if (-1 == select(maxFd + 1, &(readfds), NULL, NULL, &tv))
+ {
+ continue;
+ }
+
+ loadedPluginsItr = loadedPlugins->begin();
+
+ for ( ; loadedPluginsItr != loadedPlugins->end(); )
+ {
+ MPMCommonPluginCtx *ctx = (*loadedPluginsItr).plugin_ctx;
+ if (ctx == NULL)
+ {
+ loadedPluginsItr++;
+ continue;
+ }
+ if (ctx->started)
+ {
+ if (FD_ISSET(ctx->parent_reads_fds.read_fd, &(readfds)))
+ {
+ pid_t childStat = waitpid(ctx->child_pid, &status, WNOHANG);
+ MPMPipeMessage pipe_message;
+ ssize_t readbytes = 0;
+
+ pipe_message.payloadSize = 0;
+ pipe_message.msgType = MPM_NOMSG;
+ pipe_message.payload = NULL;
+ readbytes = MPMReadPipeMessage(ctx->parent_reads_fds.read_fd, &pipe_message);
+ if ((childStat != 0) || (readbytes <= 0))
+ {
+ OIC_LOG_V(DEBUG, TAG, "Plugin %s is exited",
+ (*loadedPluginsItr).shared_object_name);
+ if (pipe_message.payloadSize > 0)
+ {
+ OICFree((void*)pipe_message.payload);
+ pipe_message.payload = NULL;
+ pipe_message.payloadSize = 0;
+ }
+ ctx->started = false;
+ }
+ else
+ {
+ (*loadedPluginsItr).callbackClient((uint32_t)pipe_message.msgType,
+ (MPMMessage)pipe_message.payload,
+ pipe_message.payloadSize,
+ (*loadedPluginsItr).shared_object_name);
+
+ pipe_message.msgType = MPM_NOMSG;
+ if (pipe_message.payloadSize > 0)
+ {
+ OICFree((void*)pipe_message.payload);
+ pipe_message.payload = NULL;
+ pipe_message.payloadSize = 0;
+ }
+
+ }
+ }
+ }
+ loadedPluginsItr++;
+ }
+
+ sleep(1);
+ }
+
+ return (void *)loadedPlugins;
+}
+
+MPMResult MPMLoad(MPMPluginHandle *pluginHandle, const char *pluginName, MPMCallback callback,
+ const char *reconnect_file_name)
+{
+ MPMResult result = MPM_RESULT_INTERNAL_ERROR;
+ MPMPluginRunTime *functionSymbolTable;
+
+ if ((pluginName == NULL))
+ {
+ OIC_LOG(ERROR, TAG, "PluginName is NULL");
+ return result;
+ }
+
+ MPMPluginContext *plugin_instance = (MPMPluginContext *)OICCalloc(1, sizeof(MPMPluginContext));
+
+ if (plugin_instance == NULL)
+ {
+ OIC_LOG(ERROR, TAG, "Unable to allocate context.");
+ return MPM_RESULT_MEMORY_ERROR;
+ }
+ plugin_instance->plugin_ctx = NULL;
+
+ /*
+ * Now let's load the plugin and resolve the exported functions of the plugin
+ * The exported functions of interest: create, start, stop, destroy
+ */
+ plugin_instance->handle = dlopen(pluginName, RTLD_LAZY);
+
+ if (!plugin_instance->handle)
+ {
+ OIC_LOG_V(ERROR, TAG, "Error loading %s", pluginName);
+ OIC_LOG_V(ERROR, TAG, "Error message %s", dlerror());
+ goto CLEANUP;
+ }
+
+ strncpy(plugin_instance->shared_object_name, pluginName, MAX_SHARED_OBJECT_NAME_LENGTH-1);
+
+ OIC_LOG(DEBUG, TAG, "Resolving function symbols");
+ functionSymbolTable = (MPMPluginRunTime *) dlsym(plugin_instance->handle, "plugin_funcs");
+
+ if (functionSymbolTable == NULL)
+ {
+ OIC_LOG_V(ERROR, TAG, "Error loading function symbols from %s", pluginName);
+ goto CLEANUP;
+ }
+
+ plugin_instance->lifecycle.create = functionSymbolTable->create;
+ plugin_instance->lifecycle.start = functionSymbolTable->start;
+ plugin_instance->lifecycle.stop = functionSymbolTable->stop;
+ plugin_instance->lifecycle.destroy = functionSymbolTable->destroy;
+
+ // Time to call the entry points (create and start)
+ OIC_LOG_V(INFO, TAG, "Calling create on \"%s\":", pluginName);
+
+ plugin_instance->plugin_ctx = (*(plugin_instance->lifecycle.create))();
+
+ if (NULL == plugin_instance->plugin_ctx)
+ {
+ OIC_LOG_V(ERROR, TAG, "Failed to create %s", pluginName);
+ goto CLEANUP;
+ }
+ if (reconnect_file_name != NULL)
+ {
+ strncpy(plugin_instance->plugin_ctx->reconnect_file_name, reconnect_file_name,
+ MPM_MAX_FILE_NAME_LENGTH - 1);
+ }
+ else
+ {
+ plugin_instance->plugin_ctx->reconnect_file_name[0] = '\0';
+ }
+ OIC_LOG_V(INFO, TAG, "Calling start on \"%s\":", pluginName);
+
+ result = (MPMResult)(*(plugin_instance->lifecycle.start))(plugin_instance->plugin_ctx);
+ if (MPM_RESULT_OK == result)
+ {
+ OIC_LOG(INFO, TAG, "Plugin start successful.");
+ *pluginHandle = (MPMPluginHandle)(plugin_instance);
+
+ MPMPluginContext p_context;
+ p_context.lifecycle.create = plugin_instance->lifecycle.create;
+ p_context.lifecycle.start = plugin_instance->lifecycle.start;
+ p_context.lifecycle.stop = plugin_instance->lifecycle.stop;
+ p_context.lifecycle.destroy = plugin_instance->lifecycle.destroy;
+ p_context.handle = plugin_instance->handle;
+
+ p_context.plugin_ctx = plugin_instance->plugin_ctx;
+
+ strncpy(p_context.shared_object_name, plugin_instance->shared_object_name,
+ sizeof(p_context.shared_object_name) - 1);
+ p_context.shared_object_name[sizeof(p_context.shared_object_name) - 1] = '\0';
+
+ p_context.callbackClient = callback;
+
+ g_LoadedPlugins.push_back(p_context);
+
+ if (g_LoadedPlugins.size() == 1)
+ {
+ startReadResponseThread();
+ }
+
+ return result;
+ }
+
+ OIC_LOG_V(ERROR, TAG, "Failed to start \"%s\":", pluginName);
+ OIC_LOG(ERROR, TAG, "Are the authorization files correct?");
+
+CLEANUP:
+ if (plugin_instance->plugin_ctx)
+ {
+ OICFree(plugin_instance->plugin_ctx);
+ }
+ OICFree(plugin_instance);
+ return MPM_RESULT_CREATED_FAILED;
+}
+
+void startReadResponseThread()
+{
+ //Create a thread to handle the responses from plugin processes
+ int error = pthread_create(&readResponsethreadhandle, NULL, readResponse, NULL);
+ if (error != 0)
+ {
+ OIC_LOG(ERROR, TAG, "readResponse thread could not be started");
+ }
+}
+
+void stopReadResponseThread()
+{
+ /* set this variable to terminate the readResponse thread */
+ exitResponseThread = true;
+ //terminate readResponse thread
+ pthread_join(readResponsethreadhandle, NULL);
+}
+
+MPMResult MPMUnload(MPMPluginHandle pluginHandle)
+{
+ if (pluginHandle == NULL)
+ {
+ OIC_LOG(ERROR, TAG, "plugin_ctx in NULL");
+ return MPM_RESULT_INTERNAL_ERROR;
+ }
+
+ MPMPluginContext *plugin_instance = (MPMPluginContext *)(pluginHandle);
+
+ /* stop and destroy the plugin */
+ OIC_LOG_V(INFO, TAG, "Calling stop on \"%s\":", plugin_instance->shared_object_name);
+ (*(plugin_instance->lifecycle.stop))(plugin_instance->plugin_ctx);
+
+ OIC_LOG_V(INFO, TAG, "Calling destroy on \"%s\":", plugin_instance->shared_object_name);
+ (*(plugin_instance->lifecycle.destroy))(plugin_instance->plugin_ctx);
+ dlclose(plugin_instance->handle);
+
+ std::vector<MPMPluginContext>::iterator p_itr;
+ for (p_itr = g_LoadedPlugins.begin(); p_itr != g_LoadedPlugins.end(); )
+ {
+ if (strncmp((*p_itr).shared_object_name, plugin_instance->shared_object_name,
+ strlen((*p_itr).shared_object_name)) == 0)
+ {
+ OIC_LOG_V(INFO, TAG, "plugin name %s", plugin_instance->shared_object_name);
+ p_itr = g_LoadedPlugins.erase(p_itr);
+ break;
+ }
+ else
+ {
+ p_itr++;
+ }
+ }
+
+ OICFree(plugin_instance);
+
+ if (g_LoadedPlugins.size() == 0)
+ {
+ stopReadResponseThread();
+ }
+
+ return MPM_RESULT_OK;
+}
+
+/**
+ * This function performs SCAN, ADD, REMOVE and RECONNECT functionality depending on
+ * MPMMessageType paramaeter.
+ * @param[in] pluginHandle Handle of the plugin to which the message
+ * is to be sent
+ * @param[in] message Message to be sent over the pipe to the plugin
+ * @param[in] size Size of the message in bytes
+ * @param[in] type Type of the message (MPM_SCAN, MPM_ADD, etc.)
+ *
+ * @return MPM_RESULT_OK if success, MPM_RESULT_INTERNAL_ERROR upon failure
+*/
+MPMResult MPMSendPipeMessage(MPMPluginHandle pluginHandle, MPMMessage message, size_t size,
+ MPMMessageType type)
+{
+ MPMResult result = MPM_RESULT_INTERNAL_ERROR;
+ if (pluginHandle == NULL)
+ {
+ OIC_LOG(ERROR, TAG, "plugin_ctx in NULL");
+ return result;
+ }
+
+ MPMPluginContext *plugin_instance = (MPMPluginContext *)(pluginHandle);
+
+ if (plugin_instance->plugin_ctx->started)
+ {
+ OIC_LOG_V(DEBUG, TAG, "the plugin %s is started", plugin_instance->shared_object_name);
+ MPMPipeMessage pipe_message;
+
+ pipe_message.msgType = type;
+ pipe_message.payloadSize = size;
+ pipe_message.payload = (uint8_t *)message;
+ MPMCommonPluginCtx *ctx = (MPMCommonPluginCtx *) (plugin_instance->plugin_ctx);
+ result = MPMWritePipeMessage(ctx->child_reads_fds.write_fd, &pipe_message);
+
+ pipe_message.msgType = MPM_NOMSG;
+ pipe_message.payloadSize = 0;
+
+ }
+ else
+ {
+ OIC_LOG_V(ERROR, TAG, "the plugin %s is not yet started", plugin_instance->shared_object_name);
+ }
+ return result;
+}
+
+MPMResult MPMScan(MPMPluginHandle pluginHandle, MPMMessage message, size_t size)
+{
+ MPMResult result = MPM_RESULT_INTERNAL_ERROR;
+ result = MPMSendPipeMessage(pluginHandle, message, size, MPM_SCAN);
+ return result;
+}
+
+MPMResult MPMAddDevice(MPMPluginHandle pluginHandle, MPMMessage message, size_t size)
+{
+ MPMResult result = MPM_RESULT_INTERNAL_ERROR;
+ result = MPMSendPipeMessage(pluginHandle, message, size, MPM_ADD);
+ return result;
+}
+
+MPMResult MPMRemoveDevice(MPMPluginHandle pluginHandle, MPMMessage message, size_t size)
+{
+ MPMResult result = MPM_RESULT_INTERNAL_ERROR;
+ result = MPMSendPipeMessage(pluginHandle, message, size, MPM_REMOVE);
+ return result;
+}
+
+MPMResult MPMReconnectDevice(MPMPluginHandle pluginHandle, MPMMessage message, size_t size)
+{
+ MPMResult result = MPM_RESULT_INTERNAL_ERROR;
+ result = MPMSendPipeMessage(pluginHandle, message, size, MPM_RECONNECT);
+ return result;
+}
--- /dev/null
+//******************************************************************
+//
+// Copyright 2017 Intel Mobile Communications GmbH All Rights Reserved.
+//
+//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+//
+// 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.
+//
+//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+//
+
+/* This file contains the "C" plugin interface definition. This header file
+ * and its corresponding implementation file is intended to be shared among
+ * ALL plugins. Modification of this file is not necessary for the construction
+ * of a new plugin.
+ */
+
+#include <stdint.h>
+#include <stdio.h>
+#include <pthread.h>
+#include "mpmErrorCode.h"
+#include <stdbool.h>
+
+#ifndef __MINI_PLUGIN_MANAGER_H__
+#define __MINI_PLUGIN_MANAGER_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+/*******************************************************************************
+ * defines go here
+ ******************************************************************************/
+
+#define MAX_SHARED_OBJECT_NAME_LENGTH 300
+#define MAX_SHARED_OBJECTS_LOADED 20
+
+/*******************************************************************************
+ * type defines and structure definitions go here
+ ******************************************************************************/
+
+typedef void *MPMPluginHandle;
+typedef void *MPMMessage;
+
+/** MPMCallback - for receiving the responses from the plugin */
+typedef MPMCbResult (* MPMCallback)(uint32_t msgType, MPMMessage message, size_t size,
+ const char *pluginName);
+
+/**
+ * This method sends a scan request to the plugin
+ * @paran[in] pluginHandle Plugin handle for the specific plugin
+ * @param[in] message Scan request to be passed from the client to
+ * the mini_plugin_manager
+ * @param[in] size Size of the message
+ *
+ * @return MPM_RESULT_OK if no errors, else ERROR CODES defined in mpmErrorCode.h in case of error
+*/
+MPMResult MPMScan(MPMPluginHandle pluginHandle, MPMMessage message, size_t size);
+
+/**
+ * This method sends a add device request to the plugin
+ * @param[in] pluginHandle Plugin handle for the specific plugin
+ * @param[in] message Add request to be passed from the client to
+ * the mini_plugin_manager
+ * @param[in] size Size of the message
+ *
+ * @return MPM_RESULT_OK if no error, else ERROR CODES defined in mpmErrorCode.h in case of error
+*/
+MPMResult MPMAddDevice(MPMPluginHandle pluginHandle, MPMMessage message, size_t size);
+
+/**
+ * This method sends a reconnect request to the plugin
+ * @param[in] pluginHandle Plugin handle for the specific plugin
+ * @param[in] message Request request to be passed from the client to
+ * the mini_plugin_manager
+ * @param[in] size Size of the message
+ *
+ * @return MPM_RESULT_OK if no error, else ERROR CODES defined in mpmErrorCode.h in case of error
+*/
+MPMResult MPMReconnectDevice(MPMPluginHandle pluginHandle, MPMMessage message, size_t size);
+
+/**
+ * This method sends a remove device request to the plugin
+ * @param[in] pluginHandle Plugin handle for the specific plugin
+ * @param[in] message Remove request to be passed from the client to
+ * the mini_plugin_manager
+ * @param[in] size Size of the message
+ *
+ * @return MPM_RESULT_OK if no error, else ERROR CODES defined in mpmErrorCode.h in case of error
+*/
+MPMResult MPMRemoveDevice(MPMPluginHandle pluginHandle, MPMMessage message, size_t size);
+
+/**
+ * This is MPM library API to load the Plugins. It performs
+ * following functionality as:
+ * 1. Allocates memory to plugin_ctx
+ * 2. Opens shared library
+ * 3. Resolves the function symbols
+ * 4. Calls create() and start() for the plugins
+ *
+ * @param[out] pluginHandle Plugin Handle to be handed back to
+ * mpm_client application
+ * @param[in] pluginName Name of the shared library to be loaded.
+ * @param[in] callback Callback function to be invoked upon SARR responses from
+ * plugin to the client
+ * @param[in] filename Reconnect filename each plugin would maintain, currently not used
+ *
+ * @return MPM_RESULT_OK if no errors, else ERROR CODES defined in mpmErrorCode.h in case of error
+ */
+MPMResult MPMLoad(MPMPluginHandle *pluginHandle, const char *pluginName, MPMCallback callback,
+ const char *filename);
+
+/**
+ * This is MPM library API to unload the Plugins. It performs
+ * following functionality as:
+ * 1. Calls stop() and destroy() for the plugin
+ * 2. Closes shared library
+ * 3. deallocates memory allocated to plugin_ctx
+ *
+ * @param[in] pluginHandle Plugin handle sent by the
+ * mpm_client application
+ *
+ * @return MPM_RESULT_OK if no errors, MPM_RESULT_INTERNAL_ERROR if stack process error
+ */
+MPMResult MPMUnload(MPMPluginHandle pluginHandle);
+
+#ifdef __cplusplus
+}
+#endif // #ifdef __cplusplus
+
+#endif /* __MINI_PLUGIN_MANAGER_H__ */
--- /dev/null
+//******************************************************************
+//
+// Copyright 2017 Intel Mobile Communications GmbH All Rights Reserved.
+//
+//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+//
+// 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 <iostream>
+#include <unistd.h>
+#include <cstring>
+#include <string>
+#include <vector>
+#include <map>
+#include <thread>
+#include <sstream>
+#include <atomic>
+#include <csignal>
+#include <messageHandler.h>
+#include "sqlite3.h"
+#include <algorithm>
+
+#include "miniPluginManager.h"
+
+using namespace std;
+
+#define TERMINAL_BOLD_TEXT_RED "\033[1;31m"
+#define TERMINAL_ATTRIBUTE_RESET "\033[0m"
+
+#define BIND_INDEX_FIRST 1
+#define BIND_INDEX_SECOND 2
+#define BIND_INDEX_THIRD 3
+#define SQLITE_INSERT_T "INSERT INTO RECONNECT VALUES(?,?,?)"
+#define SQLITE_GET_RECONNECT_FILE "SELECT RECONNECT_FILE from RECONNECT where PLUGIN_NAME=?"
+#define SQLITE_DELETE_DEVICE "DELETE FROM RECONNECT WHERE URI=?"
+#define SQLITE_SELECT_RECONNECT_FILE "SELECT RECONNECT_FILE from RECONNECT where URI=?"
+
+#define RECONNECT_FILE_LENGTH 260
+
+map<string, MPMPluginHandle> g_loadedPlugins;
+std::atomic_bool keepEventLoop(true);
+bool autoAddMode = false;
+
+std::string rawUsageMessage =
+ R"(
+Usage: sample_app [OPTION]... PLUGINS_TO_LOAD...
+
+Load and unload plugins, send them commands after loading etc.
+Give valid inputs! This is just an example client and input is not sanitized to keep
+the client minimal.
+
+-n Start plugins in NON secure mode. Default is secure.
+
+-a Start this client in auto add mode.
+ This will cause the client send an ADD message with the same message as in
+ the scan response.
+
+-h View this message.
+)";
+
+template <typename T>
+void log(T t)
+{
+ cout << TERMINAL_BOLD_TEXT_RED << t << TERMINAL_ATTRIBUTE_RESET;
+}
+template<typename T, typename... Args>
+void log(T t, Args... args)
+{
+ cout << TERMINAL_BOLD_TEXT_RED << t << TERMINAL_ATTRIBUTE_RESET;
+ log(args...);
+}
+
+enum pluginCommands { SCAN, ADD, REMOVE, UNLOAD, MAX_COMMANDS };
+
+/**
+ * Macro to verify sqlite success.
+ * eg: VERIFY_NON_NULL(TAG, ptrData, ERROR,OC_STACK_ERROR);
+ */
+#define PDM_VERIFY_SQLITE_OK(tag, arg, logLevel) do{ if (SQLITE_OK != (arg)) \
+ { log("Error in " #arg ", Error Message: ", \
+ sqlite3_errmsg(db), "\n"); return; }}while (0)
+
+sqlite3 *db;
+
+void printUsage()
+{
+ std::cout << rawUsageMessage << "\n\n";
+}
+
+static int sqlCallback(void *data, int argc, char **argv, char **azColName)
+{
+ int i;
+
+ log("Data: ", (const char*)data, "\n");
+ for(i=0; i<argc; i++)
+ {
+ printf("%s = %s\n", azColName[i], argv[i] ? argv[i] : "NULL");
+ }
+ printf("\n");
+ return 0;
+}
+
+void fileWriteAndDbUpdate(const char * uri, const char * pluginName, const char * metadata)
+{
+ FILE *fp = NULL;
+ char filename[RECONNECT_FILE_LENGTH];
+ const char* data = "Callback function called";
+ int rc;
+ char *zErrMsg = 0;
+ std::string sql;
+ std::string changedUri = uri;
+ sqlite3_stmt *stmt = 0;
+
+ replace(changedUri.begin(), changedUri.end(), '/', '_' );
+ changedUri.erase(0,1);
+
+ memset(filename, 0, RECONNECT_FILE_LENGTH);
+ strncpy(filename, changedUri.c_str(), changedUri.length());
+ strncat(filename, ".txt", strlen(".txt")+1);
+
+ printf("filename: %s\n", filename);
+ fp = fopen(filename, "w");
+ if (!fp)
+ {
+ printf("Unable to open file");
+ return;
+ }
+ fwrite(metadata, 1, MPM_MAX_METADATA_LEN, fp);
+ fputs("\n", fp);
+ fclose(fp);
+
+ /* Create SQL statement */
+ rc = sqlite3_prepare_v2(db, SQLITE_INSERT_T,
+ strlen(SQLITE_INSERT_T) + 1, &stmt, NULL);
+ PDM_VERIFY_SQLITE_OK(TAG, rc, ERROR);
+
+ rc = sqlite3_bind_text(stmt, BIND_INDEX_FIRST, uri, MPM_MAX_URI_LEN, SQLITE_STATIC);
+ PDM_VERIFY_SQLITE_OK(TAG, rc, ERROR);
+
+ rc = sqlite3_bind_text(stmt, BIND_INDEX_SECOND, pluginName, MAX_SHARED_OBJECT_NAME_LENGTH, SQLITE_STATIC);
+ PDM_VERIFY_SQLITE_OK(TAG, rc, ERROR);
+
+ rc = sqlite3_bind_text(stmt, BIND_INDEX_THIRD, filename, RECONNECT_FILE_LENGTH, SQLITE_STATIC);
+ PDM_VERIFY_SQLITE_OK(TAG, rc, ERROR);
+
+ rc = sqlite3_step(stmt);
+ if (SQLITE_DONE != rc)
+ {
+ if (SQLITE_CONSTRAINT == rc)
+ {
+ //new OCStack result code
+ log("Error Occured: ",sqlite3_errmsg(db), "\n");
+ sqlite3_finalize(stmt);
+ return;
+ }
+ log("Error Occured: ",sqlite3_errmsg(db), "\n");
+ sqlite3_finalize(stmt);
+ return ;
+ }
+ sqlite3_finalize(stmt);
+
+ /* Create SQL statement */
+ sql = "SELECT * from RECONNECT";
+
+ /* Execute SQL statement */
+ rc = sqlite3_exec(db, sql.c_str(), sqlCallback, (void*)data, &zErrMsg);
+ if (rc != SQLITE_OK)
+ {
+ log("SQL error: ", zErrMsg, "\n");
+ sqlite3_free(zErrMsg);
+ }
+ else
+ {
+ log("Operation done successfully\n");
+ }
+
+}
+
+void deleteReconnectFile(char * uri)
+{
+ sqlite3_stmt *stmt = 0;
+ int res = 0;
+
+ res = sqlite3_prepare_v2(db, SQLITE_SELECT_RECONNECT_FILE,
+ strlen(SQLITE_SELECT_RECONNECT_FILE) + 1, &stmt, NULL);
+ PDM_VERIFY_SQLITE_OK(TAG, res, ERROR);
+ res = sqlite3_bind_text(stmt, BIND_INDEX_FIRST, uri, MPM_MAX_URI_LEN, SQLITE_STATIC);
+ PDM_VERIFY_SQLITE_OK(TAG, res, ERROR);
+
+ if (SQLITE_ROW == sqlite3_step(stmt))
+ {
+ char reconnect_file[RECONNECT_FILE_LENGTH];
+ const unsigned char *ptr = sqlite3_column_text(stmt, 0);
+ if (ptr == NULL)
+ {
+ log("An internal error occured\n");
+ return;
+ }
+ memcpy(reconnect_file, ptr, RECONNECT_FILE_LENGTH);
+ log("reconnect file to be deleted is: ", reconnect_file, "\n");
+ remove(reconnect_file);
+ }
+}
+
+void deleteRecordFromDb(char * uri)
+{
+ int res = 0;
+ sqlite3_stmt *stmt = 0;
+
+ res = sqlite3_prepare_v2(db, SQLITE_DELETE_DEVICE, strlen(SQLITE_DELETE_DEVICE) + 1, &stmt, NULL);
+ PDM_VERIFY_SQLITE_OK(TAG, res, ERROR);
+
+ res = sqlite3_bind_text(stmt, BIND_INDEX_FIRST, uri, MPM_MAX_URI_LEN, SQLITE_STATIC);
+ PDM_VERIFY_SQLITE_OK(TAG, res, ERROR);
+
+ if (SQLITE_DONE != sqlite3_step(stmt))
+ {
+ log("Delete error: ", sqlite3_errmsg(db), "\n");
+ sqlite3_finalize(stmt);
+ return;
+ }
+ log("successful", "\n");
+ sqlite3_finalize(stmt);
+}
+
+void clearDevice(char * device)
+{
+ char * uri = (char *) calloc(1, MPM_MAX_URI_LEN);
+ if (!uri)
+ {
+ log(std::string("calloc failed"), "\n");
+ return;
+ }
+ memcpy(uri, device, strlen(device));
+
+ deleteReconnectFile(uri);
+ deleteRecordFromDb(uri);
+
+ free(uri);
+}
+
+MPMCbResult onCallback(uint32_t msgType, MPMMessage message, size_t size, const char * plugin_name)
+{
+ log("Message from:", std::string(plugin_name), "\n");
+ log("Interpreting payload as string:\n");
+
+ char* message_char = (char*) calloc(1, size+1);
+ if (message_char == NULL)
+ {
+ log("Message_char is null\n");
+ return MPM_CB_RESULT_ERROR;
+ }
+ memset(message_char, 0, size+1);
+ memcpy(message_char, message, size);
+ log(std::string(message_char), "\n");
+
+ if (msgType == MPM_SCAN && autoAddMode)
+ {
+ MPMAddDevice(g_loadedPlugins[std::string(plugin_name)], message, size);
+ }
+ else if (msgType == MPM_ADD)
+ {
+ MPMAddResponse addResponse;
+ memset(&addResponse, 0, sizeof(MPMAddResponse));
+ memcpy(&addResponse, message_char, sizeof(MPMAddResponse));
+
+ log("uri: ", addResponse.uri, "\n");
+
+ fileWriteAndDbUpdate(addResponse.uri, plugin_name, addResponse.metadata);
+ }
+ else if (msgType == MPM_REMOVE)
+ {
+ clearDevice(message_char);
+ }
+
+ free(message_char);
+
+ return MPM_CB_RESULT_OK;
+}
+
+void loadPlugin(const std::string pluginPath)
+{
+ MPMPluginHandle pluginHandle;
+
+ MPMResult result = MPMLoad(&pluginHandle, pluginPath.c_str(), onCallback, NULL);
+
+ if (result == MPM_RESULT_OK)
+ {
+ g_loadedPlugins[pluginPath] = pluginHandle;
+ printf("%p\n", pluginHandle);
+ log(pluginPath, " is loaded.\n");
+ }
+ else
+ {
+ std::cout << pluginPath << " failed to load." << std::endl;
+ }
+}
+
+void printPlugins()
+{
+ log("Plugins:\n");
+ int no = 0;
+ for (map<std::string, MPMPluginHandle >::iterator itr = g_loadedPlugins.begin(); itr != g_loadedPlugins.end();
+ ++itr)
+ {
+ log("\t", no, ":", itr->first, "\n");
+ ++no;
+ }
+}
+
+void printCommands()
+{
+ log("Commands:", "\t" , SCAN , ":" , "SCAN ", ADD , ":" , "ADD ", REMOVE , ":" , "REMOVE "
+ , UNLOAD , ":" , "UNLOAD" , "\n");
+}
+
+void unloadCommand(MPMPluginHandle handle)
+{
+ std::string uri;
+ for (map<std::string, MPMPluginHandle >::iterator itr = g_loadedPlugins.begin(); itr != g_loadedPlugins.end();
+ ++itr)
+ {
+ if (itr->second == handle)
+ {
+ uri = itr->first;
+ }
+ }
+ log("Unloading ", uri, "\n");
+ MPMUnload(handle);
+ g_loadedPlugins.erase(uri);
+}
+
+void addCommand(MPMPluginHandle handle)
+{
+ std::string message;
+ log("What message to send to ADD (Usually URI of a \"scanned\" resource) : ");
+ std::cin >> message;
+
+ log("Sending ", message.c_str(), "\n");
+ MPMAddDevice(handle, const_cast<char *> (message.c_str()), message.size() + 1);
+}
+
+void removeCommand(MPMPluginHandle handle)
+{
+ std::string message;
+ log("What message to send to REMOVE (Usually URI of a \"added\" resource) : ");
+ std::cin >> message;
+
+ log("Sending ", message.c_str(), "\n");
+ MPMRemoveDevice(handle, const_cast<char *> (message.c_str()), message.size() + 1);
+}
+
+void scanCommand(MPMPluginHandle handle)
+{
+ MPMScan(handle, NULL, 0);
+}
+
+
+void processCommand (MPMPluginHandle handle, int commandNo)
+{
+ switch(commandNo)
+ {
+ case UNLOAD:
+ unloadCommand(handle);
+ break;
+ case SCAN:
+ scanCommand(handle);
+ break;
+ case ADD:
+ addCommand(handle);
+ break;
+ case REMOVE:
+ removeCommand(handle);
+ break;
+ default:
+ log("Invalid command\n");
+ break;
+ }
+}
+void getCommandEventLoop()
+{
+ while (keepEventLoop)
+ {
+ std::string input;
+ int plugin_no = -1, commandNo = -1;
+
+ std::cout << endl << endl;
+ if (g_loadedPlugins.empty())
+ {
+ log("No plugins loaded. We're done here. Bye!\n");
+ break;
+ }
+ printPlugins();
+ std::cout << "\n";
+ printCommands();
+ log("Enter blank line to see more logs.\n");
+ log("Enter plugin_no command_no : ");
+ while (getline(cin, input ))
+ {
+ if (input != "\n")
+ {
+ stringstream ss(input);
+ ss >> plugin_no >> commandNo;
+ break;
+ }
+ }
+
+ if (commandNo != -1 || plugin_no != -1)
+ {
+ MPMPluginHandle selectedPlugin = NULL;
+ int i = 0;
+
+ for (map<std::string, MPMPluginHandle >::iterator itr = g_loadedPlugins.begin();
+ itr != g_loadedPlugins.end();
+ ++itr)
+ {
+ if (i == plugin_no)
+ {
+ selectedPlugin = itr->second;
+ break;
+ }
+ ++i;
+ }
+ processCommand (selectedPlugin, commandNo);
+ }
+ }
+ for (map<std::string, MPMPluginHandle >::iterator itr = g_loadedPlugins.begin();
+ itr != g_loadedPlugins.end();
+ ++itr)
+ {
+ unloadCommand(itr->second);
+ }
+}
+
+void loadCommandLinePlugins (std::vector<string> &pluginsToLoad)
+{
+ for (uint32_t i = 0; i < pluginsToLoad.size(); ++i)
+ {
+ log("Loading ", pluginsToLoad[i], "\n");
+ loadPlugin(pluginsToLoad[i]);
+ }
+}
+
+void signalHandler(int signal)
+{
+ (void) signal;
+ keepEventLoop = false;
+}
+
+void openDatabase()
+{
+ int rc;
+ char *zErrMsg = 0;
+ std::string sql;
+ rc = sqlite3_open("reconnect.db", &db);
+ if (rc)
+ {
+ log("Can't open database: ", sqlite3_errmsg(db), "\n");
+ return;
+ }
+ else
+ {
+ log("Opened database successfully\n");
+ }
+ /* Create SQL statement */
+ sql = "CREATE TABLE IF NOT EXISTS RECONNECT(" \
+ "URI TEXT PRIMARY KEY NOT NULL," \
+ "PLUGIN_NAME TEXT NOT NULL," \
+ "RECONNECT_FILE TEXT NOT NULL);";
+
+ /* Execute SQL statement */
+ rc = sqlite3_exec(db, sql.c_str(), NULL, 0, &zErrMsg);
+ if (rc != SQLITE_OK)
+ {
+ log("SQL error: ", zErrMsg, "\n");
+ sqlite3_free(zErrMsg);
+ }
+ else
+ {
+ log("Table created successfully\n");
+ }
+}
+
+void reconnect()
+{
+ FILE *fp = NULL;
+
+ size_t len = -1;
+ int ret = 0;
+ char * metadata = NULL;
+ sqlite3_stmt *stmt = 0;
+ int res = 0;
+ for (map<std::string, MPMPluginHandle >::iterator itr = g_loadedPlugins.begin();
+ itr != g_loadedPlugins.end();
+ ++itr)
+ {
+ res = sqlite3_prepare_v2(db, SQLITE_GET_RECONNECT_FILE,
+ strlen(SQLITE_GET_RECONNECT_FILE) + 1, &stmt, NULL);
+ PDM_VERIFY_SQLITE_OK(TAG, res, ERROR);
+
+ char * plugin_name = (char *) calloc( 1, MAX_SHARED_OBJECT_NAME_LENGTH);
+ if (plugin_name == NULL)
+ {
+ log("calloc failed","\n");
+ return ;
+ }
+ memcpy(plugin_name, (*itr).first.c_str(), (*itr).first.length());
+
+ res = sqlite3_bind_text(stmt, BIND_INDEX_FIRST, plugin_name, MAX_SHARED_OBJECT_NAME_LENGTH, SQLITE_STATIC);
+ PDM_VERIFY_SQLITE_OK(TAG, res, ERROR);
+ while (SQLITE_ROW == sqlite3_step(stmt))
+ {
+ char filename[RECONNECT_FILE_LENGTH];
+ const unsigned char *ptr = sqlite3_column_text(stmt, 0);
+ if (ptr == NULL)
+ {
+ log("An internal error occured\n");
+ continue;
+ }
+ memcpy(filename, ptr, RECONNECT_FILE_LENGTH);
+ log("reconnect filename is: ", filename, "\n");
+ fp = fopen(filename, "r");
+ if (fp == NULL)
+ {
+ log("File could not be opened\n");
+ continue;
+ }
+ ret = getline(&metadata, &len, fp);
+ if (ret == -1)
+ {
+ fclose(fp);
+ continue;
+ }
+ fclose(fp);
+ MPMReconnectDevice(itr->second, metadata, MPM_MAX_METADATA_LEN);
+ }
+ printf("no columns returned\n");
+ free(metadata);
+ metadata = NULL;
+ free(plugin_name);
+ sqlite3_finalize(stmt);
+ }
+}
+
+
+int main(int argc, char const *argv[])
+{
+ vector<string> pluginsToLoad;
+ pluginsToLoad.clear();
+
+ for (int i = 1; i < argc; ++i)
+ {
+ if (strcmp(argv[i], "-n") == 0)
+ {
+ setenv("NONSECURE", "true", 1);
+ }
+ else if (strcmp(argv[i], "-a") == 0)
+ {
+ autoAddMode = true;
+ }
+ else if (strcmp(argv[i], "--help") == 0 || strcmp(argv[i], "-h") == 0)
+ {
+ printUsage();
+ return 0;
+ }
+ else
+ {
+ pluginsToLoad.push_back(std::string(argv[i]));
+ }
+ }
+
+ if (!pluginsToLoad.empty())
+ {
+ loadCommandLinePlugins(pluginsToLoad);
+ }
+ else
+ {
+ log("At least one plugin needed\n");
+ return 0;
+ }
+
+ openDatabase();
+
+ std::signal(SIGINT, signalHandler);
+
+ reconnect();
+
+ std::thread eventLoopThread = std::thread(getCommandEventLoop);
+ eventLoopThread.join();
+
+ sqlite3_close(db);
+ return 0;
+}
--- /dev/null
+General:
+The "mpm_sample_client" is intended to show how you can
+programmatically control the functionality of the
+"minipluginmanager" library.
+
+1. "mpm_sample_client" awaits user input before doing
+ anything. You will be prompted with CLI controls
+ to flex the minipluginmanager library's APIs.
+
+ The only modes of operation are controlled via
+ command line arguments upon startup.
+
+ + You may issue the flag "-n" such that all
+ resources created by any plugin are created in
+ IoTivity's non-secure mode (i.e. no DTLS
+ encryption is used for the representation of
+ that particular resource).
+ (If this flag is not passed in, all
+ resources will be created in IoTivity's
+ secure mode (all requests/responses will
+ use DTLS encryption).
+
+ + Only one plugin will be loaded with the name
+ specified
+
+ + Operations performed by the "mpm_sample_client" are
+ as follows:
+ - Scan - Device Discovery
+ - Add - Create iotivity resource
+ - Remove - Delete iotivity resource
+ - Unload - to unload the plugin and exit mpm_sample_client
--- /dev/null
+#******************************************************************
+#
+# Copyright 2017 Intel Mobile Communications GmbH All Rights Reserved.
+#
+#-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+#
+# 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.
+#
+#-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+
+Import('env')
+import os
+import os.path
+target_os = env.get('TARGET_OS')
+target_arch = env.get('TARGET_ARCH')
+mpmclient_env = env.Clone()
+src_dir = env.get('SRC_DIR')
+bridging_dir = os.path.join(src_dir, 'bridging')
+
+######################################################################
+# Build flags
+######################################################################
+
+def maskFlags(flags):
+ flags = [flags.replace('-Wl,--no-undefined', '' ) for flags in flags]
+ return flags
+mpmclient_env.PrependUnique(CPPPATH = [
+ os.path.join(bridging_dir, 'include'),
+ os.path.join(bridging_dir, 'mini_plugin_manager'),
+ os.path.join(bridging_dir, 'mpm_client')
+ ])
+
+# Note: Each of the plugin names need to be listed here after 'minipluginmanager'.
+# Otherwise you will need to prepend LD_LIBRARY_PATH to your execution command.
+mpmclient_env.PrependUnique(LIBS = ['minipluginmanager'])
+
+if target_arch in ['x86_64', 'arm64']:
+ mpmclient_env.AppendUnique(CPPFLAGS = ['-Llib64'])
+else:
+ mpmclient_env.AppendUnique(CPPFLAGS = ['-Llib'])
+mpmclient_env.AppendUnique(LIBS = ['pthread'])
+
+mpmclient_env.AppendUnique(CPPDEFINES = ['TB_LOG'])
+
+mpmclient_env.AppendUnique(LIBS = ['m',
+ 'octbstack',
+ 'ocsrm',
+ 'connectivity_abstraction',
+ 'coap',
+ 'curl' ])
+
+mpmclient_env.AppendUnique(RPATH = [os.path.join(env.get('BUILD_DIR'), 'bridging', 'plugins')])
+
+if target_os in ['linux', 'tizen']:
+ mpmclient_env.ParseConfig('pkg-config --cflags --libs sqlite3')
+else:
+ mpmclient_env.AppendUnique(CPPPATH = ['#/extlibs/sqlite3'])
+
+mpmclient_env['LINKFLAGS'] = maskFlags(env['LINKFLAGS'])
+mpmclient_env.AppendUnique(LINKFLAGS = ['-Wl,--allow-shlib-undefined'])
+######################################################################
+# Source files and Targets
+######################################################################
+mpm_sample_client = mpmclient_env.Program('mpm_sample_client',
+ ['MPMSampleClient.cpp'])
+
+list_of_samples = [mpm_sample_client]
+
+Alias("samples", list_of_samples)
+
+env.AppendTarget('samples')
+
+
--- /dev/null
+General:
+To use this plugin, a config file "lifx.cnf" needs to be populated and placed in
+the same directory as the mpm client (i.e. "mpm_client" or "mpm_client_sarr")
+generated by IoTivity's build system.
+
+Note: A LifX light bulb with a non-proxied internet connection is required to use
+this plugin. All tests/verifications have been with a "LifX Color 1000 A19" bulb.
+This plugin will NOT provision your light bulb for you. You will need to perform
+the set-up steps through the LifX app (this can be found in either the iOS or
+Android app store) and your own LifX user account.
+
+What should this file look like?
+
+ See sample "lifx.cnf.sample" file with contents as shown
+ below(without spaces, tabs or quotes):
+
+ "
+
+ cdf1519ec9998380fff7abcaf03d404401063cec7a2972dc68d4eef3e6f328e2:
+
+ "
+
+Where to put this file?
+ The placement of the lifx.cnf file should be where
+ your mpm client is also:
+
+ <iotivity>/out/<TARGET_OS>/<TARGET_ARCH>/<BUILD>/bridging/src/mpm_client
+
+ Example: <iotivity>/out/linux/x86_64/release/bridging/src/mpm_client
+ Depending on your build configuration, the path may
+ look mildly different.
+
+What is this key or token?
+
+ The LifX mapping requires usage of the LifX
+ Developer's API. The usage of this API requires
+ that every user/developer has their own HTTP API
+ Token.This HTTP API Token allows the
+ liblifxplugin.so to perform actions within your
+ LifX cloud account on your behalf.
+
+Where can I obtain the token as shown in the above example?
+
+ Use the same username and password you used to set
+ up your LifX bulb at the following address to
+ obtain a token and select "Generate Token":
+
+ https://cloud.lifx.com/settings
+
+ Append a colon ":" to the end of the token and
+ place it in your lifx.cnf file. Do not share this
+ token with anyone. This token can give someone
+ complete access to your LifX account. Be careful.
+
+How can I start this plugin?
+
+ Use the binary executable 'mpm_sample_client' to load and
+ control this plugin.
+
+ More information on these clients can be found at
+ <iotivity>/bridging/src/mpm_client/README.
+
+For proper documentation of this plugin, Mini Plugin
+Manager, the client applications, and other plugins, please
+perform a query on the "Bridging" or "Bridging Project" at
+wiki.iotivity.org.
--- /dev/null
+#******************************************************************
+#
+# Copyright 2017 Intel Mobile Communications GmbH All Rights Reserved.
+#
+#-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+#
+# 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.
+#
+#-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+##
+# LifX Plugin build script
+##
+
+import os
+import os.path
+
+Import('env')
+
+target_os = env.get('TARGET_OS')
+src_dir = env.get('SRC_DIR')
+bridging_path = os.path.join(src_dir, 'bridging')
+
+lifx_env = env.Clone()
+
+print "Reading LifX Plugin script"
+
+######################################################################
+# Build flags
+######################################################################
+
+def maskFlags(flags):
+ flags = [flags.replace('-Wl,--no-undefined', '' ) for flags in flags]
+ return flags
+
+lifx_env.PrependUnique(CPPPATH = [ os.path.join(src_dir, 'resource', 'c_common', 'oic_malloc', 'include'),
+ os.path.join(src_dir, 'resource', 'c_common', 'oic_string', 'include'),
+ os.path.join(src_dir, 'resource', 'c_common'),
+ os.path.join(src_dir, 'resource', 'oc_logger', 'include'),
+ os.path.join(src_dir, 'resource', 'csdk', 'logger', 'include'),
+ os.path.join(src_dir, 'resource', 'csdk', 'include'),
+ os.path.join(src_dir, 'resource', 'csdk', 'stack', 'include'),
+ os.path.join(src_dir, 'resource', 'include'),
+ os.path.join(src_dir, 'extlibs', 'cjson'),
+ os.path.join(src_dir, 'extlibs', 'tinycbor', 'src'),
+ os.path.join(src_dir, 'extlibs', 'rapidjson', 'rapidjson', 'include', 'rapidjson')
+ ])
+lifx_env.AppendUnique(CPPPATH = [ os.path.join(bridging_path, 'include'),
+ os.path.join(bridging_path, 'plugins', 'lifx_plugin', 'lifx_objects')
+ ])
+
+if target_os not in ['arduino', 'windows']:
+ lifx_env.AppendUnique(CPPDEFINES = ['WITH_POSIX'])
+
+if target_os in ['darwin','ios']:
+ lifx_env.AppendUnique(CPPDEFINES = ['_DARWIN_C_SOURCE'])
+
+if 'g++' in lifx_env.get('CXX'):
+ lifx_env.AppendUnique(CXXFLAGS = ['-std=c++0x', '-Wall', '-Wextra', '-Werror'])
+
+lifx_env.AppendUnique(RPATH = [lifx_env.get('BUILD_DIR')])
+lifx_env.AppendUnique(LIBPATH = [lifx_env.get('BUILD_DIR')])
+
+if lifx_env.get('LOGGING'):
+ lifx_env.AppendUnique(CPPDEFINES = ['TB_LOG'])
+
+lifx_env['LINKFLAGS'] = maskFlags(env['LINKFLAGS'])
+lifx_env.AppendUnique(LINKFLAGS = ['-Wl,--allow-shlib-undefined'])
+lifx_env.AppendUnique(LINKFLAGS = ['-Wl,--whole-archive', lifx_env.get('BUILD_DIR') +'libmpmcommon.a','-Wl,-no-whole-archive'])
+
+lifx_env.AppendUnique(LIBS = ['m',
+ 'octbstack',
+ 'ocsrm',
+ 'connectivity_abstraction',
+ 'coap',
+ 'curl' ])
+
+#####################################################################
+# Source files and Target(s)
+######################################################################
+lifx_src = [
+ os.path.join(bridging_path, 'plugins', 'lifx_plugin', 'lifxResource.cpp'),
+ os.path.join(bridging_path, 'plugins', 'lifx_plugin', 'lifx_objects', 'lifx.cpp'),
+ ]
+
+lifx_env.AppendUnique(LIFX_SRC = lifx_src)
+lifxlib = lifx_env.SharedLibrary('lifxplugin', lifx_env.get('LIFX_SRC'))
+lifx_env.InstallTarget(lifxlib, 'lifxplugin')
+lifx_env.UserInstallTargetLib(lifxlib, 'lifxplugin')
--- /dev/null
+cdf1519ec9998380fff7abcaf03d404401063cec7a2972dc68d4eef3e6f328e2:
--- /dev/null
+//******************************************************************
+//
+// Copyright 2017 Intel Mobile Communications GmbH All Rights Reserved.
+//
+//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+//
+// 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 <stdio.h>
+#include <string.h>
+#include <string>
+#include <fstream>
+#include <iostream>
+#include <map>
+#include <memory>
+#include <set>
+#include <cmath>
+#include <mutex>
+#include "pluginServer.h"
+#include "ocpayload.h"
+#include "oic_malloc.h"
+#include "oic_string.h"
+#include "lifx.h"
+#include "logger.h"
+#include "ConcurrentIotivityUtils.h"
+
+MPMPluginCtx *g_pluginCtx = NULL;
+
+#define DEVICE_NAME "LIFX_BULB"
+#define DEVICE_TYPE "LIGHT_BULB"
+#define MANUFACTURER_NAME "LIFX"
+#define BM 3
+#define MAX_ACCESS_TOKEN_LENGTH 1024
+#define TAG "LIFX_RESOURCE"
+
+static const char* OIC_BINARY_SWITCH = "oic.r.switch.binary";
+static const char* OIC_BRIGHTNESS_LIGHT = "oic.r.light.brightness";
+
+const uint BINARY_SWITCH_CALLBACK = 0;
+const uint BRIGHTNESS_CALLBACK = 1;
+
+static const std::string BINARY_SWITCH_RELATIVE_URI = "/switch";
+static const std::string BRIGHTNESS_RELATIVE_URI = "/brightness";
+
+char accessToken[MAX_ACCESS_TOKEN_LENGTH];
+const static char CRED_FILE[] = "./oic_svr_db_lifx.dat";
+
+using namespace OC::Bridging;
+
+typedef struct
+{
+ char id[MPM_MAX_LENGTH_64];
+ char uuid[MPM_MAX_LENGTH_64];
+ char label[MPM_MAX_LENGTH_64];
+ char user[MPM_MAX_LENGTH_256];
+} LightDetails;
+
+std::map<std::string, LifxLightSharedPtr> uriToLifxLightMap;
+std::map<std::string, LifxLightSharedPtr> addedLights;
+std::mutex addedLightsLock;
+
+// Forward declarations.
+static void *lightMonitorThread(void *pointer);
+OCEntityHandlerResult resourceEntityHandler(OCEntityHandlerFlag flag,
+ OCEntityHandlerRequest *request, void *callbackParam);
+
+static LifxLightSharedPtr getLifXLightFromOCFResourceUri(std::string resourceUri);
+
+FILE *lifxSecurityFile(const char *, const char *mode)
+{
+ return fopen(CRED_FILE, mode);
+}
+
+MPMResult pluginCreate(MPMPluginCtx **pluginSpecificCtx)
+{
+ if (g_pluginCtx != NULL)
+ {
+ OIC_LOG(ERROR, TAG, "Plugin is already created.");
+ return MPM_RESULT_ALREADY_CREATED;
+ }
+
+ MPMPluginCtx *ctx = (MPMPluginCtx *) OICCalloc(1, sizeof(MPMPluginCtx));
+
+ if (ctx == NULL)
+ {
+ OIC_LOG(ERROR, TAG, "Allocation of plugin context failed");
+ return MPM_RESULT_INTERNAL_ERROR;
+ }
+
+ *pluginSpecificCtx = ctx;
+ g_pluginCtx = ctx;
+
+ ctx->device_name = "Lifx Translator";
+ ctx->resource_type = "oic.d.light";
+ ctx->open = lifxSecurityFile;
+
+
+ FILE *fp = fopen("./lifx.cnf", "r");
+
+ if (NULL == fp)
+ {
+ OIC_LOG(ERROR, TAG, "error loading lifx.cnf file.");
+ return MPM_RESULT_INTERNAL_ERROR;
+ }
+
+ if (fgets(accessToken, MAX_ACCESS_TOKEN_LENGTH - 1, fp) == NULL)
+ {
+ OIC_LOG(ERROR, TAG, "Failed to read ./lifx.cnf");
+ fclose(fp);
+ return MPM_RESULT_INTERNAL_ERROR;
+ }
+ accessToken[strlen(accessToken)-1] = '\0';
+ fclose(fp);
+
+ return MPM_RESULT_OK;
+}
+
+MPMResult pluginStart(MPMPluginCtx *ctx)
+{
+ MPMResult result = MPM_RESULT_INVALID_PARAMETER;
+ int error = 0;
+
+ if (ctx->started)
+ {
+ OIC_LOG(INFO, TAG, "Plugin is already started.");
+ return MPM_RESULT_ALREADY_STARTED;
+ }
+
+ ctx->stay_in_process_loop = true;
+
+ //@todo Maybe move this to plugin_add()? once the first light is added?
+ error = pthread_create(&(ctx->thread_handle), NULL, lightMonitorThread, ctx);
+ if (error == 0)
+ {
+ ctx->started = true;
+ result = MPM_RESULT_OK;
+ }
+ else
+ {
+ OIC_LOG_V(ERROR, TAG, "Can't create plugin specific thread :[%s]", strerror(errno));
+ pluginStop(ctx);
+ result = MPM_RESULT_STARTED_FAILED;
+ }
+
+ OIC_LOG_V(INFO, TAG, "Plugin start return value:%d.", result);
+ return (result);
+}
+
+MPMResult pluginScan(MPMPluginCtx *, MPMPipeMessage *)
+{
+ std::vector<LifxLightSharedPtr> lightsScanned;
+
+ MPMResult result = LifxLight::getLights(accessToken, lightsScanned);
+
+ for (uint32_t i = 0; i < lightsScanned.size(); ++i)
+ {
+ LifxLightSharedPtr light = lightsScanned[i];
+
+ OIC_LOG_V(INFO, TAG, "Found %s bulb %s(%s).", light->state.connected ? "CONNECTED" : "OFFLINE",
+ light->config.id.c_str(), light->config.label.c_str());
+
+ if (!light->state.connected)
+ {
+ OIC_LOG(INFO, TAG, "Ignoring OFFLINE light");
+ continue;
+ }
+
+ std::string uri = "/lifx/" + light->config.id;
+
+ if (uriToLifxLightMap.find(uri) != uriToLifxLightMap.end())
+ {
+ OIC_LOG_V(INFO, TAG, "Already found %s. Ignoring", uri.c_str());
+ continue;
+ }
+
+ uriToLifxLightMap[uri] = light;
+
+ MPMSendResponse(uri.c_str(), uri.size(), MPM_SCAN);
+ }
+ if (result != MPM_RESULT_OK)
+ {
+ OIC_LOG_V(ERROR, TAG, "Failed to fetch lights with error (%d)", result);
+ return MPM_RESULT_INTERNAL_ERROR;
+ }
+
+ return MPM_RESULT_OK;
+
+}
+
+bool isSecureEnvironmentSet()
+{
+ char *non_secure_env = getenv("NONSECURE");
+
+ if (non_secure_env != NULL && (strcmp(non_secure_env, "true") == 0))
+ {
+ OIC_LOG(INFO, TAG, "Creating NON SECURE resources");
+ return false;
+ }
+ OIC_LOG(INFO, TAG, "Creating SECURE resources");
+ return true;
+}
+
+MPMResult createPayloadForMetadata(MPMResourceList **list, const char *uri, const char *res_type,
+ const char *interface)
+{
+ MPMResourceList *tempPtr;
+ tempPtr = (MPMResourceList *) OICCalloc(1, sizeof(MPMResourceList));
+ if (!tempPtr)
+ {
+ OIC_LOG_V(ERROR, TAG, "failed to allocate memory for tempPtr");
+ return MPM_RESULT_OUT_OF_MEMORY;
+ }
+ strncpy(tempPtr->rt, res_type, MPM_MAX_LENGTH_64);
+ strncpy(tempPtr->href, uri, MPM_MAX_URI_LEN);
+ strncpy(tempPtr->interfaces, interface, MPM_MAX_LENGTH_64);
+ tempPtr->bitmap = BM;
+ tempPtr->next = *list;
+ *list = tempPtr;
+ return MPM_RESULT_OK;
+}
+
+/***
+ * Creates 2 OCF resources for a LifxLight. One for the switch to turn it on and off
+ * and one for brightness.
+ *
+ * @param[in] uri Base uri. Switch and brightness uris are baseUri appended with "/switch" & "/brightness"
+ * @return MPM_RESULT_OK
+ */
+MPMResult createOCFResources(const std::string &uri)
+{
+ uint8_t resourceProperties = (OC_OBSERVABLE | OC_DISCOVERABLE);
+ if (isSecureEnvironmentSet())
+ {
+ resourceProperties |= OC_SECURE;
+ }
+
+ std::string switchUri = uri + BINARY_SWITCH_RELATIVE_URI;
+ ConcurrentIotivityUtils::queueCreateResource(switchUri, OIC_BINARY_SWITCH, OC_RSRVD_INTERFACE_ACTUATOR,
+ resourceEntityHandler,
+ (void *) BINARY_SWITCH_CALLBACK, resourceProperties);
+
+ std::string brightnessUri = uri + BRIGHTNESS_RELATIVE_URI;
+ ConcurrentIotivityUtils::queueCreateResource(brightnessUri, OIC_BRIGHTNESS_LIGHT, OC_RSRVD_INTERFACE_ACTUATOR,
+ resourceEntityHandler,
+ (void *) BRIGHTNESS_CALLBACK, resourceProperties);
+
+ return MPM_RESULT_OK;
+}
+
+MPMResult deleteOCFResources(const std::string &uri)
+{
+ ConcurrentIotivityUtils::queueDeleteResource(uri + BINARY_SWITCH_RELATIVE_URI);
+ ConcurrentIotivityUtils::queueDeleteResource(uri + BRIGHTNESS_RELATIVE_URI);
+ return MPM_RESULT_OK;
+}
+
+MPMResult pluginAdd(MPMPluginCtx *, MPMPipeMessage * message)
+{
+ if (message->payloadSize <= 0 && message->payload == NULL)
+ {
+ OIC_LOG(ERROR, TAG, "No payload received, failed to add device");
+ return MPM_RESULT_INTERNAL_ERROR;
+ }
+
+ MPMResult result = MPM_RESULT_INTERNAL_ERROR;
+ uint8_t *buff = NULL;
+ MPMResourceList *list = NULL;
+ MPMDeviceSpecificData deviceConfiguration;
+ LightDetails pluginSpecificDetails;
+ std::string user;
+ memset(&pluginSpecificDetails, 0, sizeof(LightDetails));
+ memset(&deviceConfiguration, 0, sizeof(MPMDeviceSpecificData));
+
+ std::string uri = reinterpret_cast<const char*>(message->payload);
+
+ std::lock_guard<std::mutex> lock(addedLightsLock);
+ if (addedLights.find(uri) != addedLights.end())
+ {
+ OIC_LOG_V(ERROR, TAG, "%s already added", uri.c_str());
+ return MPM_RESULT_ALREADY_CREATED;
+ }
+ if (uriToLifxLightMap.find(uri) == uriToLifxLightMap.end())
+ {
+ OIC_LOG_V(ERROR, TAG, "%s was NOT discovered in a scan", uri.c_str());
+ return MPM_RESULT_INTERNAL_ERROR;
+ }
+
+ createOCFResources(uri);
+
+ buff = (uint8_t *)OICCalloc(1, MPM_MAX_METADATA_LEN);
+ if (buff == NULL)
+ {
+ OIC_LOG_V(ERROR, TAG, "Failed to allocate memory for reconnect buffer");
+ return MPM_RESULT_OUT_OF_MEMORY;
+ }
+
+ std::string switchUri = uri + BINARY_SWITCH_RELATIVE_URI;
+ result = createPayloadForMetadata(&list, switchUri.c_str(), OIC_BINARY_SWITCH,
+ OC_RSRVD_INTERFACE_ACTUATOR);
+
+ std::string brightnessUri = uri + BRIGHTNESS_RELATIVE_URI;
+ result = createPayloadForMetadata(&list, brightnessUri.c_str(), OIC_BRIGHTNESS_LIGHT,
+ OC_RSRVD_INTERFACE_ACTUATOR);
+ if (result == MPM_RESULT_OUT_OF_MEMORY)
+ {
+ OIC_LOG(ERROR, TAG, "Failed to create payload for metadata");
+ return result;
+ }
+
+ LifxLightSharedPtr targetLight = uriToLifxLightMap[uri];
+ targetLight->getUser(user);
+
+ // filling plugin specific details
+ strncpy(pluginSpecificDetails.id, targetLight->config.id.c_str(), MPM_MAX_LENGTH_64);
+ strncpy(pluginSpecificDetails.label, targetLight->config.label.c_str(), MPM_MAX_LENGTH_64);
+ strncpy(pluginSpecificDetails.uuid, targetLight->config.uuid.c_str(), MPM_MAX_LENGTH_64);
+ strncpy(pluginSpecificDetails.user, user.c_str(), MPM_MAX_LENGTH_256);
+
+ // filling device specific details
+ strncpy(deviceConfiguration.devName, DEVICE_NAME, MPM_MAX_LENGTH_64);
+ strncpy(deviceConfiguration.devType, DEVICE_TYPE, MPM_MAX_LENGTH_64);
+ strncpy(deviceConfiguration.manufacturerName, MANUFACTURER_NAME, MPM_MAX_LENGTH_256);
+
+ MPMFormMetaData(list, &deviceConfiguration, buff, MPM_MAX_METADATA_LEN, &pluginSpecificDetails,
+ sizeof(pluginSpecificDetails));
+
+ addedLights[uri] = uriToLifxLightMap[uri];
+
+ MPMAddResponse response;
+ strncpy(response.uri, uri.c_str(), MPM_MAX_URI_LEN);
+ memcpy(response.metadata, buff, MPM_MAX_METADATA_LEN);
+
+ size_t size = sizeof(MPMAddResponse);
+
+ MPMSendResponse(&response, size, MPM_ADD);
+
+ OICFree(buff);
+ return result;
+}
+
+MPMResult pluginRemove(MPMPluginCtx *, MPMPipeMessage *message)
+{
+ if (message->payloadSize <= 0 && message->payload == NULL)
+ {
+ OIC_LOG(ERROR, TAG, "No paylaod received, failed to remove device");
+ return MPM_RESULT_INTERNAL_ERROR;
+ }
+ std::string uri = reinterpret_cast<const char *>(message->payload);
+ OIC_LOG_V(DEBUG, TAG, "device uri to be removed - %s ", uri.c_str());
+
+ std::lock_guard<std::mutex> lock(addedLightsLock);
+ if (addedLights.find(uri) == addedLights.end())
+ {
+ OIC_LOG(ERROR, TAG, "Device to be removed is not added yet");
+ return MPM_RESULT_NOT_PRESENT;
+ }
+
+ deleteOCFResources(uri);
+
+ addedLights.erase(uri);
+ uriToLifxLightMap.erase(uri);
+
+ MPMSendResponse(uri.c_str(), uri.size(), MPM_REMOVE);
+ return MPM_RESULT_OK;
+}
+
+MPMResult pluginReconnect(MPMPluginCtx *, MPMPipeMessage *message)
+{
+ MPMResourceList *list = NULL, *temp = NULL;
+ void *pluginSpecificDetails = NULL;
+
+ if (message->payloadSize <= 0 && message->payload == NULL)
+ {
+ OIC_LOG(ERROR, TAG, "No paylaod received, failed to reconnect");
+ return MPM_RESULT_INTERNAL_ERROR;
+ }
+
+ MPMParseMetaData(message->payload, MPM_MAX_METADATA_LEN, &list, &pluginSpecificDetails);
+
+ LightDetails *lightDetails = (LightDetails *)pluginSpecificDetails;
+
+ OIC_LOG_V(DEBUG, TAG, "\n Reconnect Details \nid - %s\nuuid - %s\nlabel - %s\nuser - %s\n",
+ lightDetails->id, lightDetails->uuid, lightDetails->label, lightDetails->user);
+
+ LifxLight::lightState state;
+ LifxLight::lightConfig cfg(lightDetails->id, lightDetails->uuid, lightDetails->label);
+ std::string uri = "/lifx/" + cfg.id;
+ std::shared_ptr<LifxLight> light = std::make_shared<LifxLight>(state, cfg, lightDetails->user);
+
+ createOCFResources(uri);
+ uriToLifxLightMap[uri] = light;
+ addedLights[uri] = uriToLifxLightMap[uri];
+
+ while (list)
+ {
+ temp = list;
+ list = list->next;
+ OICFree(temp);
+ }
+ free(lightDetails);
+ return MPM_RESULT_OK;
+}
+
+MPMResult pluginStop(MPMPluginCtx *pluginSpecificCtx)
+{
+ MPMResult result = MPM_RESULT_OK;
+
+ MPMPluginCtx *ctx = pluginSpecificCtx;
+
+ if (NULL != ctx && g_pluginCtx != NULL)
+ {
+ addedLights.clear();
+ uriToLifxLightMap.clear();
+
+ if (ctx->started)
+ {
+ ctx->stay_in_process_loop = false;
+ pthread_join(ctx->thread_handle, NULL);
+ ctx->started = false;
+ }
+ }
+
+ OIC_LOG_V(INFO, TAG, "Plugin stop's return value:%d", result);
+ return (result);
+
+}
+
+MPMResult pluginDestroy(MPMPluginCtx *pluginSpecificCtx)
+{
+ MPMResult result = MPM_RESULT_INTERNAL_ERROR;
+ MPMPluginCtx *ctx = (MPMPluginCtx *) pluginSpecificCtx;
+
+ if (ctx != NULL && g_pluginCtx != NULL)
+ {
+ if (ctx->started)
+ {
+ pluginStop(pluginSpecificCtx);
+ }
+ // freeing the resource allocated in create
+ OICFree(ctx);
+ g_pluginCtx = NULL;
+ result = MPM_RESULT_OK;
+ }
+
+ OIC_LOG_V(INFO, TAG, "Plugin destroy's return value:%d", result);
+
+ return (result);
+}
+
+OCRepPayload *addCommonLifXProperties(const LifxLightSharedPtr &l, OCRepPayload *payload)
+{
+ if (!OCRepPayloadSetPropString(payload, "x.com.intel.label", l->config.label.c_str()))
+ {
+ throw std::runtime_error("failed to set label");
+ }
+
+ if (!OCRepPayloadSetPropDouble(payload, "x.com.intel.secondsSinceLastSeen",
+ l->state.secondsSinceLastSeen))
+ {
+ throw std::runtime_error("failed to set secondsSinceLastSeen");
+ }
+ return payload;
+}
+
+OCRepPayload *getBinarySwitchPayload(LifxLightSharedPtr l)
+{
+ std::unique_ptr<OCRepPayload , decltype(OCRepPayloadDestroy) *> payload {OCRepPayloadCreate(),
+ OCRepPayloadDestroy };
+
+ if (!payload)
+ {
+ throw std::runtime_error("payload cannot be NULL");
+ }
+
+ if (!OCRepPayloadSetPropBool(payload.get(), "value", l->state.power))
+ {
+ throw std::runtime_error("failed to set binary switch value in the payload");
+ }
+ return addCommonLifXProperties(l, payload.release());
+}
+
+OCRepPayload *getBrightnessPayload(LifxLightSharedPtr l)
+{
+ std::unique_ptr<OCRepPayload , decltype(OCRepPayloadDestroy) *> payload {OCRepPayloadCreate(),
+ OCRepPayloadDestroy };
+ if (!payload)
+ {
+ throw std::runtime_error("payload cannot be NULL");
+ }
+
+ // Convert LifX [0.0, 1.0] to OCF [0, 100].
+ if (!OCRepPayloadSetPropInt(payload.get(), "brightness", (int64_t) (l->state.brightness * 100.0)))
+ {
+ throw std::runtime_error("failed to set brightness");
+ }
+ return addCommonLifXProperties(l, payload.release());
+
+}
+
+OCRepPayload *processGetRequest(LifxLightSharedPtr l, uintptr_t resourceTypeInCallback)
+{
+ if (resourceTypeInCallback == BINARY_SWITCH_CALLBACK)
+ {
+ return getBinarySwitchPayload(l);
+ }
+ else if (resourceTypeInCallback == BRIGHTNESS_CALLBACK)
+ {
+ return getBrightnessPayload(l);
+ }
+ return NULL;
+}
+
+OCEntityHandlerResult processBinarySwitchUpdate(OCRepPayload *payload, LifxLightSharedPtr l)
+{
+ bool power = false;
+ if (!OCRepPayloadGetPropBool(payload, "value", &power))
+ {
+ throw std::runtime_error("Payload must contain \"value\"");
+ }
+
+ MPMResult result = l->setPower(power);
+
+ if (result != MPM_RESULT_OK)
+ {
+ throw std::runtime_error("Error setting power for PUT request");
+ }
+ return OC_EH_OK;
+}
+
+OCEntityHandlerResult processBrightnessUpdate(OCRepPayload *payload, LifxLightSharedPtr l)
+{
+ int64_t ocfBrightness = 0;
+ if (!OCRepPayloadGetPropInt(payload, "brightness", &ocfBrightness))
+ {
+ throw std::runtime_error("Payload must contain \"brightness\"");
+ }
+ // OCF brightness is [0, 100] and Lifx is [0.0, 1.00]
+ double lifxBrightness = ocfBrightness / 100.0;
+
+ MPMResult result = l->setBrightness(lifxBrightness);
+
+ if (result != MPM_RESULT_OK)
+ {
+ throw std::runtime_error("Error setting brightness for PUT request");
+ }
+ return OC_EH_OK;
+}
+
+OCEntityHandlerResult processPutRequest(OCRepPayload *payload, LifxLightSharedPtr l,
+ uintptr_t resourceTypeInCallback)
+{
+ if (payload == NULL)
+ {
+ throw std::runtime_error("PUT payload cannot be NULL");
+ }
+ if (resourceTypeInCallback == BINARY_SWITCH_CALLBACK)
+ {
+ return processBinarySwitchUpdate(payload, l);
+ }
+ else if (resourceTypeInCallback == BRIGHTNESS_CALLBACK)
+ {
+ return processBrightnessUpdate(payload, l);
+ }
+ return OC_EH_OK;
+}
+
+static LifxLightSharedPtr getLifXLightFromOCFResourceUri(std::string resourceUri)
+{
+ OIC_LOG_V(INFO, TAG, "Request for %s", resourceUri.c_str());
+ std::lock_guard<std::mutex> lock(addedLightsLock);
+ for (auto uriToLifXPair : addedLights)
+ {
+ if (resourceUri.find(uriToLifXPair.first) != std::string::npos)
+ {
+ return uriToLifXPair.second;
+ }
+ }
+ throw std::runtime_error("Resource " + resourceUri + " not found");
+}
+
+OCEntityHandlerResult resourceEntityHandler(OCEntityHandlerFlag ,
+ OCEntityHandlerRequest *request,
+ void *cb)
+{
+ uintptr_t callBackParamResourceType = (uintptr_t) cb;
+ OCEntityHandlerResult result = OC_EH_OK;
+ MPMResult res = MPM_RESULT_OK;
+
+ try
+ {
+ std::string uri;
+ ConcurrentIotivityUtils::getUriFromHandle(request->resource, uri);
+
+ LifxLightSharedPtr targetLight = getLifXLightFromOCFResourceUri(uri);
+
+ switch (request->method)
+ {
+ case OC_REST_GET:
+ // Empty GET case as actual request will be processed after the switch case.
+ break;
+
+ case OC_REST_PUT:
+ case OC_REST_POST:
+
+ res = (MPMResult)processPutRequest((OCRepPayload *) request->payload, targetLight,
+ callBackParamResourceType);
+ if (res != MPM_RESULT_OK)
+ result = OC_EH_ERROR;
+ break;
+
+ default:
+ OIC_LOG_V(INFO, TAG, "Unsupported method (%d) recieved", request->method);
+ ConcurrentIotivityUtils::respondToRequestWithError(request, "Unsupported method received",
+ OC_EH_METHOD_NOT_ALLOWED);
+ return OC_EH_OK;
+ }
+
+ OCRepPayload *responsePayload = processGetRequest(targetLight, callBackParamResourceType);
+ ConcurrentIotivityUtils::respondToRequest(request, responsePayload, result);
+ OCRepPayloadDestroy(responsePayload);
+ }
+ catch (const std::exception &exp)
+ {
+ ConcurrentIotivityUtils::respondToRequestWithError(request, exp.what(), OC_EH_ERROR);
+ return OC_EH_OK;
+ }
+
+ return OC_EH_OK;
+}
+
+/* This function does not look for new lights. Only monitors existing lights.
+ * @param[in] pluginSpecificCtx plugin specific context
+ */
+void *lightMonitorThread(void *pluginSpecificCtx)
+{
+ MPMPluginCtx *ctx = (MPMPluginCtx *) pluginSpecificCtx;
+ if (ctx != NULL)
+ {
+ while (ctx->stay_in_process_loop)
+ {
+ addedLightsLock.lock();
+
+ for (auto itr : addedLights)
+ {
+ LifxLightSharedPtr l = itr.second;
+ if (!l)
+ {
+ continue;
+ }
+ LifxLight::lightState oldState = l->state;
+
+ l->refreshState();
+
+ LifxLight::lightState newState = l->state;
+
+ if (oldState.power != newState.power)
+ {
+ ConcurrentIotivityUtils::queueNotifyObservers(itr.first + BINARY_SWITCH_RELATIVE_URI);
+ }
+ if (fabs(oldState.brightness - newState.brightness) >
+ 0.00001) // Lazy epsilon for double equals check.
+ {
+ ConcurrentIotivityUtils::queueNotifyObservers(itr.first + BRIGHTNESS_RELATIVE_URI);
+ }
+ if (oldState.connected != newState.connected)
+ {
+ OIC_LOG_V(INFO, TAG, "%s is %s", l->config.id.c_str(),
+ l->state.connected ? "ONLINE" : "OFFLINE");
+ }
+ }
+
+ addedLightsLock.unlock();
+ sleep(MPM_THREAD_PROCESS_SLEEPTIME);
+ }
+ OIC_LOG(INFO, TAG, "Leaving LIFX monitor thread");
+ }
+ pthread_exit(NULL);
+}
--- /dev/null
+//******************************************************************
+//
+// Copyright 2017 Intel Mobile Communications GmbH All Rights Reserved.
+//
+//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+//
+// 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 "stringbuffer.h"
+#include "writer.h"
+#include "curlClient.h"
+#include "iotivity_config.h"
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include "document.h"
+#include <memory>
+#include <stdexcept>
+#include <iostream>
+#include "lifx.h"
+#include "logger.h"
+
+#define TAG "LIFX"
+using namespace OC::Bridging;
+
+MPMResult parseCloudResponse(const std::string response)
+{
+ rapidjson::Document doc;
+ doc.SetObject();
+
+ if (doc.Parse(response.c_str()).HasParseError())
+ {
+ OIC_LOG(ERROR, TAG, "Parse error in set Power");
+ return MPM_RESULT_JSON_ERROR;
+ }
+
+ if (doc.HasMember("error"))
+ {
+ throw std::runtime_error(doc["error"].GetString());
+ }
+
+ if (doc.HasMember("results"))
+ {
+ const rapidjson::Value &results = doc["results"];
+
+ if (results.Size() != 1)
+ {
+ OIC_LOG(INFO, TAG, "Multi element results. Only considering the first");
+ }
+
+ for (rapidjson::SizeType i = 0; i < results.Size(); ++i)
+ {
+ // This'll only consider the first one anyway as we only expect 1 element
+ // This'll have to be changed when we can change multiple light states at once.
+ std::string status = results[i]["status"].GetString();
+
+ return status == "ok" ? MPM_RESULT_OK : MPM_RESULT_INTERNAL_ERROR;
+ }
+ }
+ return MPM_RESULT_OK;
+}
+
+MPMResult static parseLightsFromCloudResponse(const std::string response, const std::string user,
+ std::vector<std::shared_ptr<LifxLight>> &parsedLights)
+{
+ rapidjson::Document lightsJsonResponse;
+ lightsJsonResponse.SetObject();
+
+ if (lightsJsonResponse.Parse<0>(response.c_str()).HasParseError())
+ {
+ OIC_LOG_V(ERROR, TAG, "Error parsing JSON:\n%s", response.c_str());
+ return MPM_RESULT_JSON_ERROR;
+ }
+
+ if (!lightsJsonResponse.IsArray())
+ {
+ OIC_LOG_V(ERROR, TAG, "Response is not an array.\n%s", response.c_str());
+ return MPM_RESULT_JSON_ERROR;
+ }
+ if (lightsJsonResponse.Empty())
+ {
+ OIC_LOG(ERROR, TAG, "Response is empty");
+ return MPM_RESULT_JSON_ERROR;
+ }
+
+ int i = 0;
+
+ for (rapidjson::Value::ConstValueIterator itr = lightsJsonResponse.Begin();
+ itr != lightsJsonResponse.End(); ++itr)
+ {
+ if (lightsJsonResponse[i].IsObject())
+ {
+ LifxLight::lightConfig cfg;
+ cfg.id = lightsJsonResponse[i]["id"].GetString();
+ cfg.uuid = lightsJsonResponse[i]["uuid"].GetString();
+ cfg.label = lightsJsonResponse[i]["label"].GetString();
+
+ LifxLight::lightState state;
+
+ std::string powerStr = lightsJsonResponse[i]["power"].GetString();
+
+ state.power = powerStr == "on";
+
+ state.brightness = lightsJsonResponse[i]["brightness"].GetDouble();
+ state.connected = lightsJsonResponse[i]["connected"].GetBool();
+ state.secondsSinceLastSeen = lightsJsonResponse[i]["seconds_since_seen"].GetDouble();
+
+ std::shared_ptr<LifxLight> light = std::make_shared<LifxLight>(state, cfg, user);
+
+ parsedLights.push_back(light);
+
+ ++i;
+ }
+ else
+ {
+ OIC_LOG(ERROR, TAG, "Json blob doesn't have an expected object to be parsed.");
+ return MPM_RESULT_INTERNAL_ERROR;
+ }
+ }
+
+ return MPM_RESULT_OK;
+}
+
+MPMResult LifxLight::setState(std::string &stateRequest)
+{
+ if (this->user.empty())
+ {
+ throw std::runtime_error("Light not created in valid state by constructor. No \"user\" found");
+ }
+
+ CurlClient cc = CurlClient(CurlClient::CurlMethod::PUT, uri + "/state")
+ .addRequestHeader(CURL_HEADER_ACCEPT_JSON)
+ .setUserName(user)
+ .setRequestBody(stateRequest);
+
+ OIC_LOG_V(INFO, TAG, "Request %s", stateRequest.c_str());
+ OIC_LOG_V(INFO, TAG, "Uri %s", uri.c_str());
+
+ int curlCode = cc.send();
+ std::string response = cc.getResponseBody();
+
+ OIC_LOG_V(INFO, TAG, "Response %s", response.c_str());
+
+ if (curlCode != MPM_RESULT_OK)
+ {
+ OIC_LOG_V(ERROR, TAG, "PUT request for power failed. Error code %d", curlCode);
+ return MPM_RESULT_INTERNAL_ERROR;
+ }
+ // Small sleep to let the light inform the cloud of any state changes.
+ sleep(2);
+ refreshState();
+
+ return parseCloudResponse(response);
+}
+
+MPMResult LifxLight::setPower(bool power)
+{
+ std::string setPowerRequest = std::string("power=") + (power ? "on" : "off");
+ return setState(setPowerRequest);
+}
+
+MPMResult LifxLight::setBrightness(double brightness)
+{
+ if (brightness < 0.0) brightness = 0.0;
+ else if (brightness > 1.0) brightness = 1.0;
+
+ std::string setBrightnessRequest = std::string("brightness=") + std::to_string(brightness);
+
+ return setState(setBrightnessRequest);
+}
+
+// Limit calls to refreshState. LifX APIs limit calls
+// from an access token to 60 calls every 60 seconds
+// as made clear @ https://api.developer.lifx.com/docs/rate-limits
+MPMResult LifxLight::refreshState()
+{
+ if (this->user.empty())
+ {
+ throw std::runtime_error("Light not created in valid state by constructor. No \"user\" found");
+ }
+
+ CurlClient cc = CurlClient(CurlClient::CurlMethod::GET, uri)
+ .addRequestHeader(CURL_HEADER_ACCEPT_JSON)
+ .setUserName(user);
+
+ int curlCode = cc.send();
+
+ if (curlCode != MPM_RESULT_OK)
+ {
+ OIC_LOG_V(ERROR, TAG, "GET request for light failed with error %d", curlCode);
+ return MPM_RESULT_INTERNAL_ERROR;
+ }
+
+ std::string response = cc.getResponseBody();
+
+ std::vector<std::shared_ptr<LifxLight>> parsedLights;
+ MPMResult parseResult = parseLightsFromCloudResponse(response, this->user, parsedLights);
+
+ if (parseResult != MPM_RESULT_OK)
+ {
+ return parseResult;
+ }
+
+ if (parsedLights.size() != 1)
+ {
+ OIC_LOG_V(ERROR, TAG, "This is irregular! Instead of 1 light, returned %ld " , parsedLights.size());
+ return MPM_RESULT_JSON_ERROR;
+ }
+
+ std::shared_ptr<LifxLight> light = parsedLights[0]; // Only 1 light expected here.
+
+ if (light->config.uuid != this->config.uuid)
+ {
+ OIC_LOG_V(ERROR, TAG, "%s in response, when request made by %s", light->config.uuid.c_str(),
+ this->config.uuid.c_str());
+ return MPM_RESULT_JSON_ERROR;
+ }
+
+ this->state = light->state;
+ return MPM_RESULT_OK;
+}
+
+MPMResult LifxLight::getLights(const std::string user,
+ std::vector<std::shared_ptr<LifxLight> > &lights)
+{
+ CurlClient cc = CurlClient(CurlClient::CurlMethod::GET, LIFX_LIST_LIGHTS_URI)
+ .addRequestHeader(CURL_HEADER_ACCEPT_JSON)
+ .setUserName(user);
+
+ int curlCode = cc.send();
+
+ if (curlCode != MPM_RESULT_OK)
+ {
+ OIC_LOG_V(ERROR, TAG, "GET request for lights failed. Error code %d", curlCode);
+ return MPM_RESULT_INTERNAL_ERROR;
+ }
+
+ std::string response = cc.getResponseBody();
+
+ return parseLightsFromCloudResponse(response, user, lights);
+}
+
--- /dev/null
+//******************************************************************
+//
+// Copyright 2017 Intel Mobile Communications GmbH All Rights Reserved.
+//
+//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+//
+// 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.
+//
+//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+//
+
+#ifndef __LIFX_H__
+#define __LIFX_H__
+
+
+#include <vector>
+#include <typeinfo>
+#include <mpmErrorCode.h>
+
+#define LIFX_BASE_URI "https://api.lifx.com/v1/lights"
+#define LIFX_LIST_LIGHTS_URI LIFX_BASE_URI "/all"
+
+class LifxLight
+{
+ public:
+
+ typedef struct _lightState
+ {
+ double brightness;
+ bool power;
+ bool connected;
+ double secondsSinceLastSeen;
+ } lightState;
+
+ typedef struct _lightConfig
+ {
+ std::string id;
+ std::string uuid;
+ std::string label;
+ _lightConfig() {}
+ _lightConfig(std::string light_id, std::string light_uuid, std::string light_label)
+ {
+ id = light_id;
+ uuid = light_uuid;
+ label = light_label;
+ }
+ } lightConfig;
+
+ LifxLight() {}
+ virtual ~LifxLight() {}
+
+ LifxLight(lightState &state, lightConfig &config, const std::string &user)
+ {
+ this->user = user;
+ this->state = state;
+ this->config = config;
+ this->uri = std::string(LIFX_BASE_URI).append("/").append(config.id);
+ }
+
+ /*Limit calls to refreshState. LifX API's limit calls
+ * from an access token and only allow 60 calls every 60 seconds
+ * as made clear @ https://api.developer.lifx.com/docs/rate-limits
+ */
+ MPMResult refreshState();
+
+ MPMResult setPower(bool power);
+
+ /**
+ * Sets the brightness for this bulb.
+ * @param[in] brightness Is in the range [0.0, 1.0].
+ * Values outside the interval are set to the nearest endpoint.
+ * @return MPM_RESULT_OK if success, else appropriate error code on error
+ */
+ MPMResult setBrightness(double brightness);
+
+ MPMResult static getLights(const std::string user,
+ std::vector<std::shared_ptr<LifxLight> > &lights);
+
+ void getUser(std::string &userID)
+ {
+ userID = user;
+ }
+
+ lightState state;
+ lightConfig config;
+ std::string uri;
+
+ private:
+ std::string user;
+
+ MPMResult setState(std::string &setPowerRequest);
+};
+
+typedef std::shared_ptr<LifxLight> LifxLightSharedPtr;
+
+#endif // __LIFX_H__
--- /dev/null
+//******************************************************************
+//
+// Copyright 2017 Intel Mobile Communications GmbH All Rights Reserved.
+//
+//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+//
+// 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 <stdio.h>
+#include <string.h>
+#include <string>
+#include <fstream>
+#include <iostream>
+#include <set>
+#include <assert.h>
+#include <pluginServer.h>
+#include "logger.h"
+
+#define TAG "STUB_PLUGIN"
+
+MPMPluginCtx *g_plugin_ctx = NULL;
+static char CRED_FILE[] = "./oic_svr_db_sample.dat";
+
+FILE *sec_file(const char *, const char *mode)
+{
+ return fopen(CRED_FILE, mode);
+}
+
+MPMResult pluginCreate(MPMPluginCtx **pluginSpecificCtx)
+{
+ MPMPluginCtx *ctx = (MPMPluginCtx *) calloc(1, sizeof(MPMPluginCtx));
+ if (ctx == NULL)
+ {
+ OIC_LOG(ERROR, TAG, "Allocation of plugin context failed");
+ return MPM_RESULT_INTERNAL_ERROR;
+ }
+ *pluginSpecificCtx = ctx;
+ g_plugin_ctx = ctx;
+ ctx->device_name = "Stub plugin";
+ ctx->resource_type = "oic.d.stub";
+ ctx->open = sec_file;
+ return MPM_RESULT_OK;
+}
+
+MPMResult pluginStart(MPMPluginCtx *ctx)
+{
+ ctx->stay_in_process_loop = true;
+ OIC_LOG(INFO, TAG, "Plugin start called!");
+ return MPM_RESULT_OK;
+}
+
+void echoResponse(MPMPipeMessage *message, std::string type)
+{
+ std::string s = type + " response echo";
+ MPMSendResponse(s.c_str(), s.size(), message->msgType);
+}
+
+MPMResult pluginScan(MPMPluginCtx *, MPMPipeMessage *message)
+{
+ OIC_LOG(INFO, TAG, "Scan called!");
+ // Send back scan response to the client.
+ echoResponse(message, "SCAN");
+ return MPM_RESULT_OK;
+}
+
+MPMResult pluginAdd(MPMPluginCtx *, MPMPipeMessage *message)
+{
+ OIC_LOG(INFO, TAG, "Add called! Create Iotivity resources here based on what the client says");
+ echoResponse(message, "ADD");
+ return MPM_RESULT_OK;
+}
+
+MPMResult pluginRemove(MPMPluginCtx *, MPMPipeMessage *message)
+{
+ OIC_LOG(INFO, TAG, "Remove called! Remove iotivity resources here based on what the client says");
+ echoResponse(message, "REMOVE");
+ return MPM_RESULT_OK;
+}
+
+MPMResult pluginReconnect(MPMPluginCtx *, MPMPipeMessage *message)
+{
+ OIC_LOG(INFO, TAG,
+ "Reconnect called! Reconnect to devices, create resources from the message/cloud/db/file.");
+ echoResponse(message, "ADD");
+ return MPM_RESULT_OK;
+}
+
+MPMResult pluginStop(MPMPluginCtx *)
+{
+ OIC_LOG(INFO, TAG, "Stop called !");
+ return MPM_RESULT_OK;
+}
+
+
+MPMResult pluginDestroy(MPMPluginCtx *pluginSpecificCtx)
+{
+ OIC_LOG(INFO, TAG, "Destroy called");
+ if (!pluginSpecificCtx)
+ {
+ assert(g_plugin_ctx);
+
+ if (pluginSpecificCtx->started)
+ {
+ pluginStop(pluginSpecificCtx);
+ }
+ // freeing the resource allocated in create
+ free(pluginSpecificCtx);
+ g_plugin_ctx = NULL;
+ }
+
+ return (MPM_RESULT_OK);
+}
--- /dev/null
+import os, string, sys
+
+Import('env')
+
+env = env.Clone()
+rapidjson_env = env.Clone()
+
+target_os = env.get('TARGET_OS')
+target_arch = env.get('TARGET_ARCH')
+
+host_os = sys.platform
+
+######################################################################
+# Build flags
+######################################################################
+src_dir = env.get('SRC_DIR')
+path = os.path.join(src_dir, 'extlibs', 'rapidjson', 'rapidjson')
+
+# check 'rapidjson' library, if it doesn't exits, ask user to download it
+if not os.path.exists(path):
+ rapidjson_env = Environment(ENV = os.environ)
+ rapidjson_zip = env.Download('v1.0.2.zip', 'https://github.com/miloyip/rapidjson/archive/v1.0.2.zip')
+ rapidjson_dir = env.UnpackAll('rapidjson', rapidjson_zip)
+ os.system("mv rapidjson-1.0.2 rapidjson")
# define OC_CLOSE_SOCKET(s) close(s)
#endif
+#ifndef SIZE_MAX
+/* Some systems fail to define SIZE_MAX in <stdint.h>, even though C99 requires it...
+ * Conversion from signed to unsigned is defined in 6.3.1.3 (Signed and unsigned integers) p2,
+ * which says: "the value is converted by repeatedly adding or subtracting one more than the
+ * maximum value that can be represented in the new type until the value is in the range of the
+ * new type."
+ * So -1 gets converted to size_t by adding SIZE_MAX + 1, which results in SIZE_MAX.
+ */
+# define SIZE_MAX ((size_t)-1)
+#endif
+
#endif