Drop use of two deprecated defines
[iotivity.git] / bridging / common / pluginServer.cpp
1 //******************************************************************
2 //
3 // Copyright 2017 Intel Mobile Communications GmbH All Rights Reserved.
4 //
5 //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
6 //
7 // Licensed under the Apache License, Version 2.0 (the "License");
8 // you may not use this file except in compliance with the License.
9 // You may obtain a copy of the License at
10 //
11 //      http://www.apache.org/licenses/LICENSE-2.0
12 //
13 // Unless required by applicable law or agreed to in writing, software
14 // distributed under the License is distributed on an "AS IS" BASIS,
15 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 // See the License for the specific language governing permissions and
17 // limitations under the License.
18 //
19 //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
20 //
21
22 /* This file contains the plugin server implementation.  Most of this
23  * implementation is reused for other plugins.  There is NO customization
24  * required of functions in this file to accommodate plugin specific code.
25  */
26
27 #include <stdlib.h>
28 #include <stdio.h>
29 #include <string.h>
30 #include <errno.h>
31 #include "iotivity_config.h"
32 #ifdef HAVE_UNISTD_H
33 #include <unistd.h>
34 #endif
35
36 #include "messageHandler.h"
37 #include "pluginServer.h"
38 #include "oic_malloc.h"
39 #include "pluginIf.h"
40 #include "ocpayload.h"
41 #include "experimental/logger.h"
42 #include "WorkQueue.h"
43 #include "ConcurrentIotivityUtils.h"
44 #include <iostream>
45 #include <octypes.h>
46
47 #define TAG "PLUGIN_SERVER"
48 #define OC_KEY_VALUE_DELIMITER             "="
49
50 using namespace OC::Bridging;
51
52 //function prototypes
53
54 /**
55  * This function initializes all the boilerplate items for the OCF server.
56  * it relies strictly on the "C" layer of the OCF stack.  This is common
57  * code for all plugins.
58  *
59  * @param[in] ctx            The context structure used by this implementation
60  * @param[in] deviceName     Name of the device as defined by the plugin
61  * @param[in] resourceType   Resource type as defined by the plugin
62  *
63  * @returns MPM_RESULT_OK if no errors, MPM_RESULT_INTERNAL_ERROR if initialization failure
64  */
65 static MPMResult initInfrastructure(MPMCommonPluginCtx *ctx, const char *deviceName,
66                                     const char *resourceType);
67
68 /**
69  * this function is a blocking function that holds the iotivity processing
70  * loop. this OCF server relies strictly on the "C" layer of the OCF stack.
71  * This is common code for all plugins.
72  *
73  * @param[in,out] result     Child process status
74  * @param[in]     ctx        The context structure used by this implementation
75  *
76  * @return MPM_RESULT_OK if no errors, MPM_RESULT_INTERNAL_ERROR if maintenance failure
77  */
78 static MPMResult maintainInfrastructure(MPMResult result, MPMCommonPluginCtx *ctx);
79
80 const char *g_date_of_manufacture = NULL;
81 const char *g_firmware_version = NULL;
82 const char *g_manufacturer_name = "Intel";
83 const char *g_operating_system_version = NULL;
84 const char *g_hardware_version = NULL;
85 const char *g_platform_id = "ce530bf4-40ab-11e6-9cca-ff46602aca08";
86 const char *g_manufacturer_url = NULL;
87 const char *g_model_number = NULL;
88 const char *g_platform_version = NULL;
89 const char *g_support_url = NULL;
90 const char *g_version = NULL;
91 const char *g_system_time = NULL;
92
93 static pthread_t processMessageFromPipeThread;
94
95 /* plugin specific context storage point.  the plugin owns the allocation
96  * and freeing of its own context
97  */
98 MPMPluginCtx *g_plugin_context = NULL;
99
100
101 /* Secure Virtual Resource database for Iotivity Server
102  * It contains Server's Identity and the PSK credentials
103  * of other devices which the server trusts
104  */
105 extern MPMCommonPluginCtx *g_com_ctx;
106
107 std::unique_ptr<ConcurrentIotivityUtils> iotivityUtils = NULL;
108
109 /**
110  * This is a non blocking pipe read function
111  *
112  * @param[in] fd            file descriptor from where messages are to be read
113  * @param[in] com_ctx       common context
114  * @param[in] ctx           plugin specific context
115  *
116  * @return false if STOP request has come from the MPM, true if STOP request
117  *         has not come from the MPM
118  */
119 bool processMessagesFromMPM(int fd, MPMCommonPluginCtx *com_ctx, MPMPluginCtx *ctx)
120 {
121     struct timeval tv;
122     fd_set fdset;
123     int nfd = -1;
124     ssize_t nbytes = 0;
125     bool shutdown = false;
126     MPMPipeMessage pipe_message;
127     g_com_ctx = com_ctx;
128
129     tv.tv_sec = 15;
130     tv.tv_usec = 0;
131
132     pipe_message.payloadSize = 0;
133     pipe_message.msgType = MPM_NOMSG;
134     pipe_message.payload = NULL;
135
136     FD_ZERO(&(fdset));
137     FD_SET(fd, &(fdset));
138     nfd = select(fd + 1, &(fdset), NULL, NULL, &tv);
139     if (nfd == -1)
140     {
141         OIC_LOG_V(ERROR, TAG, "select error: %s", strerror(errno));
142     }
143     else
144     {
145         if (FD_ISSET(fd, &(fdset)))
146         {
147             nbytes = MPMReadPipeMessage(fd, &pipe_message);
148             if (nbytes == 0)
149             {
150                 OIC_LOG(DEBUG, TAG, "EOF was read and file descriptor was found to be closed");
151                 shutdown = true;
152             }
153             if (nbytes > 0)
154             {
155                 if (pipe_message.msgType == MPM_STOP)
156                 {
157                     shutdown =  true;
158                 }
159                 else
160                 {
161                     MPMRequestHandler(&pipe_message, ctx);
162                 }
163             }
164
165             OICFree((void*)pipe_message.payload);
166
167         }
168     }
169     return (shutdown);
170 }
171
172 MPMResult MPMPluginService(MPMCommonPluginCtx *ctx)
173 {
174     MPMResult result = MPM_RESULT_INTERNAL_ERROR;
175     if (ctx == NULL)
176     {
177         OIC_LOG(ERROR, TAG, "Plugin context is NULL");
178         goto HandleError;
179     }
180
181     // plugin create is in the individual plugin.
182     result = pluginCreate(&g_plugin_context);
183
184     if (result != MPM_RESULT_OK || g_plugin_context == NULL)
185     {
186         OIC_LOG_V(ERROR, TAG, "Creation failed result: %d", result);
187         goto HandleError;
188     }
189
190     // initialize the OCF infrastructure to be setup for a server
191     result = initInfrastructure(ctx, g_plugin_context->device_name, g_plugin_context->resource_type);
192
193     if (result != MPM_RESULT_OK)
194     {
195         OIC_LOG_V(ERROR, TAG, "Error (%d) initializing OCF infrastructure", result);
196         goto HandleError;
197     }
198     if ('\0' != ctx->reconnect_file_name[0])
199     {
200         strncpy(g_plugin_context->reconnect_file_name, ctx->reconnect_file_name,
201                 strlen(ctx->reconnect_file_name));
202     }
203     else
204     {
205         memset(g_plugin_context->reconnect_file_name, 0, MPM_MAX_FILE_NAME_LENGTH);
206     }
207     // plugin start is in the individual plugin.
208     result = pluginStart(g_plugin_context);
209
210     if (result != MPM_RESULT_OK)
211     {
212         OIC_LOG_V(ERROR, TAG, "Failed to start %s plugin result: %d", g_plugin_context->device_name,
213                   result);
214     }
215
216 HandleError :
217     /* Sends the status of the child to Parent and
218      * in the case of success it act as blocking call.
219     */
220     result = maintainInfrastructure(result, ctx);
221
222     return (result);
223 }
224
225 static MPMResult setPlatformInfoParams(OCPlatformInfo &platform_info)
226 {
227     if ((strlen(g_manufacturer_name) > MAX_PLATFORM_NAME_LENGTH))
228     {
229         OIC_LOG(ERROR, TAG, "Manufacture name string length exceeded max length");
230         return MPM_RESULT_INTERNAL_ERROR;
231     }
232
233     if (g_manufacturer_url != NULL && (strlen(g_manufacturer_url) > MAX_PLATFORM_URL_LENGTH))
234     {
235         OIC_LOG(ERROR, TAG, "Url string length exceeded max length");
236         return MPM_RESULT_INTERNAL_ERROR;
237     }
238
239     platform_info.platformID = const_cast<char *> (g_platform_id);
240     platform_info.manufacturerName = const_cast<char *> (g_manufacturer_name);
241     platform_info.manufacturerUrl = const_cast<char *> (g_manufacturer_url);
242     platform_info.modelNumber = const_cast<char *> (g_model_number);
243     platform_info.dateOfManufacture = const_cast<char *> (g_date_of_manufacture);
244     platform_info.platformVersion = const_cast<char *> (g_platform_version);
245     platform_info.operatingSystemVersion = const_cast<char *> (g_operating_system_version);
246     platform_info.hardwareVersion = const_cast<char *> (g_hardware_version);
247     platform_info.firmwareVersion = const_cast<char *> (g_firmware_version);
248     platform_info.supportUrl = const_cast<char *> (g_support_url);
249     platform_info.systemTime = const_cast<char *> (g_system_time);
250
251     return MPM_RESULT_OK;
252 }
253
254 static MPMResult setDeviceInfoParams(const char *deviceName, const char *resourceType,
255                                      OCDeviceInfo &device_info)
256 {
257     if (!deviceName || deviceName[0] == '\0')
258     {
259         OIC_LOG(ERROR, TAG, "Device name is NULL");
260         return MPM_RESULT_INVALID_PARAMETER;
261     }
262     device_info.deviceName = const_cast<char *> (deviceName);
263
264     OCStringLL *stringll = NULL;
265
266     OCResourcePayloadAddStringLL(&stringll, OC_RSRVD_RESOURCE_TYPE_DEVICE);
267     OCResourcePayloadAddStringLL(&stringll, resourceType);
268
269     device_info.types = stringll;
270
271     device_info.dataModelVersions = NULL;
272     device_info.specVersion = NULL;
273
274     return MPM_RESULT_OK;
275 }
276
277 static MPMResult initInfrastructure(MPMCommonPluginCtx *ctx, const char *deviceName,
278                                     const char *resourceType)
279 {
280     MPMResult result = MPM_RESULT_INTERNAL_ERROR;
281
282     uint16_t port = 0;
283
284     if (ctx != NULL)
285     {
286         /* Create the unnamed pipe listener thread that will clear the
287          * child's stay in the loop variable when the parent wants to
288          * shut down the child process.
289          */
290         // Initialize Persistent Storage for SVR database
291         static OCPersistentStorage ps = {g_plugin_context->open, fread, fwrite, fclose, unlink};
292         OCRegisterPersistentStorageHandler(&ps);
293
294         OCStackResult ocResult = OCInit(NULL, port, OC_SERVER);
295         if (ocResult != OC_STACK_OK)
296         {
297             OIC_LOG(ERROR, TAG, "OCStack init error");
298             return result;
299         }
300
301         std::unique_ptr<WorkQueue<std::unique_ptr<IotivityWorkItem>>> q =
302             std::unique_ptr<WorkQueue<std::unique_ptr<IotivityWorkItem>>>
303             (new WorkQueue<std::unique_ptr<IotivityWorkItem>>());
304
305         iotivityUtils =  make_unique<ConcurrentIotivityUtils>(std::move(q));
306         iotivityUtils->startWorkerThreads();
307
308         OCPlatformInfo platform_info;
309         if (setPlatformInfoParams(platform_info) != MPM_RESULT_OK)
310         {
311             OIC_LOG(ERROR, TAG, "Platform info setting failed locally!");
312             return result;
313         }
314
315         if (OCSetPlatformInfo(platform_info) != OC_STACK_OK)
316         {
317             OIC_LOG(ERROR, TAG, "Platform Registration failed!");
318             return result;
319         }
320
321         OCDeviceInfo device_info = {NULL, NULL, NULL, NULL};
322         if (setDeviceInfoParams(deviceName, resourceType, device_info) != MPM_RESULT_OK)
323         {
324             OIC_LOG(ERROR, TAG, "Device info setting failed locally!");
325             return result;
326         }
327
328         if (OCSetDeviceInfo(device_info) != OC_STACK_OK)
329         {
330             OIC_LOG(ERROR, TAG, "Device Registration failed!");
331             return result;
332         }
333         // Iotivity does not take ownership of 'types' and clones itself a copy.
334         OCFreeOCStringLL(device_info.types);
335     }
336     return MPM_RESULT_OK;
337 }
338
339 void *processMessageFromPipeThreadProc(void *arg)
340 {
341     MPMCommonPluginCtx *ctx = (MPMCommonPluginCtx *)arg;
342     while (true)
343     {
344         bool result = processMessagesFromMPM(ctx->child_reads_fds.read_fd, ctx, g_plugin_context);
345         if (result != MPM_RESULT_OK)
346         {
347             OIC_LOG(INFO, TAG, "Leaving processMessageFromPipeThreadProc ");
348             break;
349         }
350     }
351     pthread_exit(NULL);
352     return NULL;
353 }
354
355 MPMResult maintainInfrastructure(MPMResult result, MPMCommonPluginCtx *ctx)
356 {
357     MPMPipeMessage pipe_message;
358     void *res;
359
360     if (ctx != NULL)
361     {
362         /* child tells status to parent prior to getting in the
363          * processing loop
364          */
365         if (result == MPM_RESULT_OK)
366         {
367             pipe_message.msgType = MPM_DONE;
368             pipe_message.payloadSize = 0;
369             pipe_message.payload = NULL;
370             result = MPMWritePipeMessage(ctx->parent_reads_fds.write_fd, &pipe_message);
371         }
372         else
373         {
374             pipe_message.msgType = MPM_ERROR;
375             pipe_message.payloadSize = 0;
376             pipe_message.payload = NULL;
377             result = MPMWritePipeMessage(ctx->parent_reads_fds.write_fd, &pipe_message);
378         }
379
380         if (result == MPM_RESULT_OK)
381         {
382             pthread_create(&processMessageFromPipeThread, NULL, processMessageFromPipeThreadProc, ctx);
383             pthread_join(processMessageFromPipeThread, &res);
384         }
385         else
386         {
387             return result;
388         }
389
390         OIC_LOG_V(INFO, TAG, "Stopping %s plugin", g_plugin_context->device_name);
391
392         result = pluginStop(g_plugin_context);
393
394         if (result != MPM_RESULT_OK)
395         {
396             OIC_LOG_V(ERROR, TAG, "Error(%d) stopping plugin %s", result, g_plugin_context->device_name);
397         }
398
399         result = pluginDestroy(g_plugin_context);
400         if (result != MPM_RESULT_OK)
401         {
402             OIC_LOG_V(ERROR, TAG, "Error(%d) stopping plugin %s", result, g_plugin_context->device_name);
403         }
404
405         iotivityUtils->stopWorkerThreads();
406
407         OCStackResult ocResult = OCStop();
408
409         if (ocResult != OC_STACK_OK)
410         {
411             OIC_LOG_V(ERROR, TAG, "Error(%d) stopping Iotivity", ocResult);
412             result = MPM_RESULT_INTERNAL_ERROR;
413         }
414     }
415     else
416     {
417         OIC_LOG(ERROR, TAG, "Bad context handed to maintain ocf infrastructure");
418     }
419     return (result);
420 }
421
422 MPMResult MPMExtractFiltersFromQuery(char *query, char **filterOne, char **filterTwo)
423 {
424
425     char *key = NULL;
426     char *value = NULL;
427     char *restOfQuery = NULL;
428     int numKeyValuePairsParsed = 0;
429
430     *filterOne = NULL;
431     *filterTwo = NULL;
432
433     if (!query)
434     {
435         // This is fine. This call is stemming from a non-entity handler request.
436         return MPM_RESULT_OK;
437     }
438
439     char *keyValuePair = strtok_r(query, OC_QUERY_SEPARATOR, &restOfQuery);
440
441     while (keyValuePair)
442     {
443         if (numKeyValuePairsParsed >= 2)
444         {
445             OIC_LOG(ERROR, TAG, "More than 2 queries params in URI.");
446             return MPM_RESULT_INVALID_PARAMETER;
447         }
448
449         key = strtok_r(keyValuePair, OC_KEY_VALUE_DELIMITER, &value);
450
451         if (!key || !value)
452         {
453             return MPM_RESULT_INVALID_PARAMETER;
454         }
455         else if (strncasecmp(key, OC_RSRVD_INTERFACE, sizeof(OC_RSRVD_INTERFACE) - 1) == 0)
456         {
457             *filterOne = value;     // if
458         }
459         else if (strncasecmp(key, OC_RSRVD_RESOURCE_TYPE, sizeof(OC_RSRVD_INTERFACE) - 1) == 0)
460         {
461             *filterTwo = value;     // rt
462         }
463         else
464         {
465             OIC_LOG_V(ERROR, TAG, "Unsupported query key: %s", key);
466             return MPM_RESULT_INVALID_PARAMETER;
467         }
468         ++numKeyValuePairsParsed;
469
470         keyValuePair = strtok_r(NULL, OC_QUERY_SEPARATOR, &restOfQuery);
471     }
472     if (*filterOne != NULL || *filterTwo != NULL)
473     {
474         OIC_LOG_V(ERROR, TAG, "Extracted params if: %s and rt: %s.", *filterOne, *filterTwo);
475     }
476
477     return MPM_RESULT_OK;
478 }