[IOT-3296] Disable CertificateRequest at MFG OTM
[iotivity.git] / resource / examples / simpleserver.cpp
1 //******************************************************************
2 //
3 // Copyright 2014 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 sample provides steps to define an interface for a resource
23 /// (properties and methods) and host this resource on the server.
24 ///
25 #include "iotivity_config.h"
26
27 #include <functional>
28 #ifdef HAVE_UNISTD_H
29 #include <unistd.h>
30 #endif
31 #ifdef HAVE_PTHREAD_H
32 #include <pthread.h>
33 #endif
34 #include <mutex>
35 #include <condition_variable>
36
37 #include "OCPlatform.h"
38 #include "OCApi.h"
39 #ifdef HAVE_WINDOWS_H
40 #include <windows.h>
41 #endif
42
43 #include "ocpayload.h"
44
45 using namespace OC;
46 using namespace std;
47 namespace PH = std::placeholders;
48
49 static const char* SVR_DB_FILE_NAME = "./oic_svr_db_server.dat";
50 int gObservation = 0;
51 void * ChangeLightRepresentation (void *param);
52 void * handleSlowResponse (void *param, std::shared_ptr<OCResourceRequest> pRequest);
53
54 // Set of strings for each of platform Info fields
55 std::string gPlatformId = "0A3E0D6F-DBF5-404E-8719-D6880042463A";
56 std::string gManufacturerName = "OCF";
57 std::string gManufacturerLink = "https://www.iotivity.org";
58 std::string gModelNumber = "myModelNumber";
59 std::string gDateOfManufacture = "2016-01-15";
60 std::string gPlatformVersion = "myPlatformVersion";
61 std::string gOperatingSystemVersion = "myOS";
62 std::string gHardwareVersion = "myHardwareVersion";
63 std::string gFirmwareVersion = "1.0";
64 std::string gSupportLink = "https://www.iotivity.org";
65 std::string gSystemTime = "2016-01-15T11.01";
66
67 // Set of strings for each of device info fields
68 std::string  deviceName = "IoTivity Simple Server";
69 std::string  deviceType = "oic.wk.tv";
70 std::string  specVersion = "ocf.1.1.0";
71 std::vector<std::string> dataModelVersions = {"ocf.res.1.1.0", "ocf.sh.1.1.0"};
72 std::string  protocolIndependentID = "fa008167-3bbf-4c9d-8604-c9bcb96cb712";
73
74 // OCPlatformInfo Contains all the platform info to be stored
75 OCPlatformInfo platformInfo;
76
77 // Specifies where to notify all observers or list of observers
78 // false: notifies all observers
79 // true: notifies list of observers
80 bool isListOfObservers = false;
81
82 // Specifies secure or non-secure
83 // false: non-secure resource
84 // true: secure resource
85 bool isSecure = false;
86
87 /// Specifies whether Entity handler is going to do slow response or not
88 bool isSlowResponse = false;
89
90 // Forward declaring the entityHandler
91
92 /// This class represents a single resource named 'lightResource'. This resource has
93 /// two simple properties named 'state' and 'power'
94
95 class LightResource
96 {
97
98 public:
99     /// Access this property from a TB client
100     std::string m_name;
101     bool m_state;
102     int m_power;
103     std::string m_lightUri;
104     OCResourceHandle m_resourceHandle;
105     OCRepresentation m_lightRep;
106     ObservationIds m_interestedObservers;
107
108 public:
109     /// Constructor
110     LightResource()
111         :m_name("John's light"), m_state(false), m_power(0), m_lightUri("/a/light"),
112                 m_resourceHandle(nullptr) {
113         // Initialize representation
114         m_lightRep.setUri(m_lightUri);
115
116         m_lightRep.setValue("state", m_state);
117         m_lightRep.setValue("power", m_power);
118         m_lightRep.setValue("name", m_name);
119     }
120
121     /* Note that this does not need to be a member function: for classes you do not have
122     access to, you can accomplish this with a free function: */
123
124     /// This function internally calls registerResource API.
125     void createResource()
126     {
127         //URI of the resource
128         std::string resourceURI = m_lightUri;
129         //resource type name. In this case, it is light
130         std::string resourceTypeName = "core.light";
131         // resource interface.
132         std::string resourceInterface = DEFAULT_INTERFACE;
133
134         // OCResourceProperty is defined ocstack.h
135         uint8_t resourceProperty;
136         if(isSecure)
137         {
138             resourceProperty = OC_DISCOVERABLE | OC_OBSERVABLE | OC_SECURE;
139         }
140         else
141         {
142             resourceProperty = OC_DISCOVERABLE | OC_OBSERVABLE;
143         }
144         EntityHandler cb = std::bind(&LightResource::entityHandler, this,PH::_1);
145
146         // This will internally create and register the resource.
147         OCStackResult result = OCPlatform::registerResource(
148                                     m_resourceHandle, resourceURI, resourceTypeName,
149                                     resourceInterface, cb, resourceProperty);
150
151         if (OC_STACK_OK != result)
152         {
153             cout << "Resource creation was unsuccessful\n";
154         }
155     }
156
157     OCStackResult createResource1()
158     {
159         // URI of the resource
160         std::string resourceURI = "/a/light1";
161         // resource type name. In this case, it is light
162         std::string resourceTypeName = "core.light";
163         // resource interface.
164         std::string resourceInterface = DEFAULT_INTERFACE;
165
166         // OCResourceProperty is defined ocstack.h
167         uint8_t resourceProperty;
168         if(isSecure)
169         {
170             resourceProperty = OC_DISCOVERABLE | OC_OBSERVABLE | OC_SECURE;
171         }
172         else
173         {
174             resourceProperty = OC_DISCOVERABLE | OC_OBSERVABLE;
175         }
176         EntityHandler cb = std::bind(&LightResource::entityHandler, this,PH::_1);
177
178         OCResourceHandle resHandle;
179
180         // This will internally create and register the resource.
181         OCStackResult result = OCPlatform::registerResource(
182                                     resHandle, resourceURI, resourceTypeName,
183                                     resourceInterface, cb, resourceProperty);
184
185         if (OC_STACK_OK != result)
186         {
187             cout << "Resource creation was unsuccessful\n";
188         }
189
190         return result;
191     }
192
193     OCResourceHandle getHandle()
194     {
195         return m_resourceHandle;
196     }
197
198     // Puts representation.
199     // Gets values from the representation and
200     // updates the internal state
201     void put(OCRepresentation& rep)
202     {
203         try {
204             if (rep.getValue("state", m_state))
205             {
206                 cout << "\t\t\t\t" << "state: " << m_state << endl;
207             }
208             else
209             {
210                 cout << "\t\t\t\t" << "state not found in the representation" << endl;
211             }
212
213             if (rep.getValue("power", m_power))
214             {
215                 cout << "\t\t\t\t" << "power: " << m_power << endl;
216             }
217             else
218             {
219                 cout << "\t\t\t\t" << "power not found in the representation" << endl;
220             }
221         }
222         catch (exception& e)
223         {
224             cout << e.what() << endl;
225         }
226
227     }
228
229     // Post representation.
230     // Post can create new resource or simply act like put.
231     // Gets values from the representation and
232     // updates the internal state
233     OCRepresentation post(OCRepresentation& rep)
234     {
235         static int first = 1;
236
237         // for the first time it tries to create a resource
238         if(first)
239         {
240             first = 0;
241
242             if(OC_STACK_OK == createResource1())
243             {
244                 OCRepresentation rep1;
245                 rep1.setValue("createduri", std::string("/a/light1"));
246
247                 return rep1;
248             }
249         }
250
251         // from second time onwards it just puts
252         put(rep);
253         return get();
254     }
255
256
257     // gets the updated representation.
258     // Updates the representation with latest internal state before
259     // sending out.
260     OCRepresentation get()
261     {
262         m_lightRep.setValue("state", m_state);
263         m_lightRep.setValue("power", m_power);
264
265         return m_lightRep;
266     }
267
268     void addType(const std::string& type) const
269     {
270         OCStackResult result = OCPlatform::bindTypeToResource(m_resourceHandle, type);
271         if (OC_STACK_OK != result)
272         {
273             cout << "Binding TypeName to Resource was unsuccessful\n";
274         }
275     }
276
277     void addInterface(const std::string& iface) const
278     {
279         OCStackResult result = OCPlatform::bindInterfaceToResource(m_resourceHandle, iface);
280         if (OC_STACK_OK != result)
281         {
282             cout << "Binding TypeName to Resource was unsuccessful\n";
283         }
284     }
285
286 private:
287 // This is just a sample implementation of entity handler.
288 // Entity handler can be implemented in several ways by the manufacturer
289 OCEntityHandlerResult entityHandler(std::shared_ptr<OCResourceRequest> request)
290 {
291     cout << "\tIn Server CPP entity handler:\n";
292     OCEntityHandlerResult ehResult = OC_EH_ERROR;
293     if(request)
294     {
295         // Get the request type and request flag
296         std::string requestType = request->getRequestType();
297         int requestFlag = request->getRequestHandlerFlag();
298
299         if(requestFlag & RequestHandlerFlag::RequestFlag)
300         {
301             cout << "\t\trequestFlag : Request\n";
302             auto pResponse = std::make_shared<OC::OCResourceResponse>();
303             pResponse->setRequestHandle(request->getRequestHandle());
304             pResponse->setResourceHandle(request->getResourceHandle());
305
306             // Check for query params (if any)
307             QueryParamsMap queries = request->getQueryParameters();
308
309             if (!queries.empty())
310             {
311                 std::cout << "\nQuery processing upto entityHandler" << std::endl;
312             }
313             for (auto it : queries)
314             {
315                 std::cout << "Query key: " << it.first << " value : " << it.second
316                         << std:: endl;
317             }
318
319             // If the request type is GET
320             if(requestType == "GET")
321             {
322                 cout << "\t\t\trequestType : GET\n";
323                 if(isSlowResponse) // Slow response case
324                 {
325                     static int startedThread = 0;
326                     if(!startedThread)
327                     {
328                         std::thread t(handleSlowResponse, (void *)this, request);
329                         startedThread = 1;
330                         t.detach();
331                     }
332                     ehResult = OC_EH_SLOW;
333                 }
334                 else // normal response case.
335                 {
336
337                     pResponse->setResponseResult(OC_EH_OK);
338                     pResponse->setResourceRepresentation(get());
339                     if(OC_STACK_OK == OCPlatform::sendResponse(pResponse))
340                     {
341                         ehResult = OC_EH_OK;
342                     }
343                 }
344             }
345             else if(requestType == "PUT")
346             {
347                 cout << "\t\t\trequestType : PUT\n";
348                 OCRepresentation rep = request->getResourceRepresentation();
349
350                 // Do related operations related to PUT request
351                 // Update the lightResource
352                 put(rep);
353
354                 pResponse->setResponseResult(OC_EH_OK);
355                 pResponse->setResourceRepresentation(get());
356                 if(OC_STACK_OK == OCPlatform::sendResponse(pResponse))
357                 {
358                     ehResult = OC_EH_OK;
359                 }
360             }
361             else if(requestType == "POST")
362             {
363                 cout << "\t\t\trequestType : POST\n";
364
365                 OCRepresentation rep = request->getResourceRepresentation();
366
367                 // Do related operations related to POST request
368                 OCRepresentation rep_post = post(rep);
369                 pResponse->setResourceRepresentation(rep_post);
370
371                 if(rep_post.hasAttribute("createduri"))
372                 {
373                     pResponse->setResponseResult(OC_EH_RESOURCE_CREATED);
374                     pResponse->setNewResourceUri(rep_post.getValue<std::string>("createduri"));
375                 }
376                 else
377                 {
378                     pResponse->setResponseResult(OC_EH_OK);
379                 }
380
381                 if(OC_STACK_OK == OCPlatform::sendResponse(pResponse))
382                 {
383                     ehResult = OC_EH_OK;
384                 }
385             }
386             else if(requestType == "DELETE")
387             {
388                 cout << "Delete request received" << endl;
389             }
390         }
391
392         if(requestFlag & RequestHandlerFlag::ObserverFlag)
393         {
394             ObservationInfo observationInfo = request->getObservationInfo();
395             if(ObserveAction::ObserveRegister == observationInfo.action)
396             {
397                 m_interestedObservers.push_back(observationInfo.obsId);
398             }
399             else if(ObserveAction::ObserveUnregister == observationInfo.action)
400             {
401                 m_interestedObservers.erase(std::remove(
402                                                             m_interestedObservers.begin(),
403                                                             m_interestedObservers.end(),
404                                                             observationInfo.obsId),
405                                                             m_interestedObservers.end());
406             }
407
408 #if defined(_WIN32)
409             DWORD threadId = 0;
410             HANDLE threadHandle = INVALID_HANDLE_VALUE;
411 #else
412             pthread_t threadId;
413 #endif
414
415             cout << "\t\trequestFlag : Observer\n";
416             gObservation = 1;
417             static int startedThread = 0;
418
419             // Observation happens on a different thread in ChangeLightRepresentation function.
420             // If we have not created the thread already, we will create one here.
421             if(!startedThread)
422             {
423 #if defined(_WIN32)
424                 threadHandle = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)ChangeLightRepresentation, (void*)this, 0, &threadId);
425 #else
426                 pthread_create (&threadId, NULL, ChangeLightRepresentation, (void *)this);
427 #endif
428                 startedThread = 1;
429             }
430             ehResult = OC_EH_OK;
431         }
432     }
433     else
434     {
435         std::cout << "Request invalid" << std::endl;
436     }
437
438     return ehResult;
439 }
440
441 };
442
443 // ChangeLightRepresentaion is an observation function,
444 // which notifies any changes to the resource to stack
445 // via notifyObservers
446 void * ChangeLightRepresentation (void *param)
447 {
448     LightResource* lightPtr = (LightResource*) param;
449
450     // This function continuously monitors for the changes
451     while (1)
452     {
453         sleep (3);
454
455         if (gObservation)
456         {
457             // If under observation if there are any changes to the light resource
458             // we call notifyObservors
459             //
460             // For demostration we are changing the power value and notifying.
461             lightPtr->m_power += 10;
462
463             cout << "\nPower updated to : " << lightPtr->m_power << endl;
464             cout << "Notifying observers with resource handle: " << lightPtr->getHandle() << endl;
465
466             OCStackResult result = OC_STACK_OK;
467
468             if(isListOfObservers)
469             {
470                 std::shared_ptr<OCResourceResponse> resourceResponse =
471                             {std::make_shared<OCResourceResponse>()};
472
473                 resourceResponse->setResourceRepresentation(lightPtr->get(), DEFAULT_INTERFACE);
474
475                 result = OCPlatform::notifyListOfObservers(  lightPtr->getHandle(),
476                                                              lightPtr->m_interestedObservers,
477                                                              resourceResponse);
478             }
479             else
480             {
481                 result = OCPlatform::notifyAllObservers(lightPtr->getHandle());
482             }
483
484             if(OC_STACK_NO_OBSERVERS == result)
485             {
486                 cout << "No More observers, stopping notifications" << endl;
487                 gObservation = 0;
488             }
489         }
490     }
491
492     return NULL;
493 }
494
495 void DeletePlatformInfo()
496 {
497     delete[] platformInfo.platformID;
498     delete[] platformInfo.manufacturerName;
499     delete[] platformInfo.manufacturerUrl;
500     delete[] platformInfo.modelNumber;
501     delete[] platformInfo.dateOfManufacture;
502     delete[] platformInfo.platformVersion;
503     delete[] platformInfo.operatingSystemVersion;
504     delete[] platformInfo.hardwareVersion;
505     delete[] platformInfo.firmwareVersion;
506     delete[] platformInfo.supportUrl;
507     delete[] platformInfo.systemTime;
508 }
509
510 void DuplicateString(char ** targetString, std::string sourceString)
511 {
512     *targetString = new char[sourceString.length() + 1];
513     strncpy(*targetString, sourceString.c_str(), (sourceString.length() + 1));
514 }
515
516 OCStackResult SetPlatformInfo(std::string platformID, std::string manufacturerName,
517         std::string manufacturerUrl, std::string modelNumber, std::string dateOfManufacture,
518         std::string platformVersion, std::string operatingSystemVersion,
519         std::string hardwareVersion, std::string firmwareVersion, std::string supportUrl,
520         std::string systemTime)
521 {
522     DuplicateString(&platformInfo.platformID, platformID);
523     DuplicateString(&platformInfo.manufacturerName, manufacturerName);
524     DuplicateString(&platformInfo.manufacturerUrl, manufacturerUrl);
525     DuplicateString(&platformInfo.modelNumber, modelNumber);
526     DuplicateString(&platformInfo.dateOfManufacture, dateOfManufacture);
527     DuplicateString(&platformInfo.platformVersion, platformVersion);
528     DuplicateString(&platformInfo.operatingSystemVersion, operatingSystemVersion);
529     DuplicateString(&platformInfo.hardwareVersion, hardwareVersion);
530     DuplicateString(&platformInfo.firmwareVersion, firmwareVersion);
531     DuplicateString(&platformInfo.supportUrl, supportUrl);
532     DuplicateString(&platformInfo.systemTime, systemTime);
533
534     return OC_STACK_OK;
535 }
536
537 OCStackResult SetDeviceInfo()
538 {
539     OCStackResult result = OC_STACK_ERROR;
540
541     OCResourceHandle handle = OCPlatform::getResourceHandleAtUri(OC_RSRVD_DEVICE_URI);
542     if (handle == NULL)
543     {
544         cout << "Failed to find resource " << OC_RSRVD_DEVICE_URI << endl;
545         return result;
546     }
547
548     result = OCPlatform::bindTypeToResource(handle, deviceType);
549     if (result != OC_STACK_OK)
550     {
551         cout << "Failed to add device type" << endl;
552         return result;
553     }
554
555     result = OCPlatform::setPropertyValue(PAYLOAD_TYPE_DEVICE, OC_RSRVD_DEVICE_NAME, deviceName);
556     if (result != OC_STACK_OK)
557     {
558         cout << "Failed to set device name" << endl;
559         return result;
560     }
561
562     result = OCPlatform::setPropertyValue(PAYLOAD_TYPE_DEVICE, OC_RSRVD_DATA_MODEL_VERSION,
563                                           dataModelVersions);
564     if (result != OC_STACK_OK)
565     {
566         cout << "Failed to set data model versions" << endl;
567         return result;
568     }
569
570     result = OCPlatform::setPropertyValue(PAYLOAD_TYPE_DEVICE, OC_RSRVD_SPEC_VERSION, specVersion);
571     if (result != OC_STACK_OK)
572     {
573         cout << "Failed to set spec version" << endl;
574         return result;
575     }
576
577     result = OCPlatform::setPropertyValue(PAYLOAD_TYPE_DEVICE, OC_RSRVD_PROTOCOL_INDEPENDENT_ID,
578                                           protocolIndependentID);
579     if (result != OC_STACK_OK)
580     {
581         cout << "Failed to set piid" << endl;
582         return result;
583     }
584
585     return OC_STACK_OK;
586 }
587
588 void * handleSlowResponse (void *param, std::shared_ptr<OCResourceRequest> pRequest)
589 {
590     // This function handles slow response case
591     LightResource* lightPtr = (LightResource*) param;
592     // Induce a case for slow response by using sleep
593     std::cout << "SLOW response" << std::endl;
594     sleep (10);
595
596     auto pResponse = std::make_shared<OC::OCResourceResponse>();
597     pResponse->setRequestHandle(pRequest->getRequestHandle());
598     pResponse->setResourceHandle(pRequest->getResourceHandle());
599     pResponse->setResourceRepresentation(lightPtr->get());
600
601     pResponse->setResponseResult(OC_EH_OK);
602
603     // Set the slow response flag back to false
604     isSlowResponse = false;
605     OCPlatform::sendResponse(pResponse);
606     return NULL;
607 }
608
609 void PrintUsage()
610 {
611     std::cout << std::endl;
612     std::cout << "Usage : simpleserver <value>\n";
613     std::cout << "    Default - Non-secure resource and notify all observers\n";
614     std::cout << "    1 - Non-secure resource and notify list of observers\n\n";
615     std::cout << "    2 - Secure resource and notify all observers\n";
616     std::cout << "    3 - Secure resource and notify list of observers\n\n";
617     std::cout << "    4 - Non-secure resource, GET slow response, notify all observers\n";
618 }
619
620 static FILE* client_open(const char* path, const char* mode)
621 {
622     char const * filename = path;
623     if (0 == strcmp(path, OC_SECURITY_DB_DAT_FILE_NAME))
624     {
625         filename = SVR_DB_FILE_NAME;
626     }
627     else if (0 == strcmp(path, OC_INTROSPECTION_FILE_NAME))
628     {
629         filename = "simpleserver_introspection.dat";
630     }
631     return fopen(filename, mode);
632 }
633
634 int main(int argc, char* argv[])
635 {
636     PrintUsage();
637     OCPersistentStorage ps {client_open, fread, fwrite, fclose, unlink };
638
639     if (argc == 1)
640     {
641         isListOfObservers = false;
642         isSecure = false;
643     }
644     else if (argc == 2)
645     {
646         int value = atoi(argv[1]);
647         switch (value)
648         {
649             case 1:
650                 isListOfObservers = true;
651                 isSecure = false;
652                 break;
653             case 2:
654                 isListOfObservers = false;
655                 isSecure = true;
656                 break;
657             case 3:
658                 isListOfObservers = true;
659                 isSecure = true;
660                 break;
661             case 4:
662                 isSlowResponse = true;
663                 break;
664             default:
665                 break;
666        }
667      }
668     else
669     {
670         return -1;
671     }
672
673     // Create PlatformConfig object
674     PlatformConfig cfg {
675         OC::ServiceType::InProc,
676         OC::ModeType::Server,
677         &ps
678     };
679
680     cfg.transportType = static_cast<OCTransportAdapter>(OCTransportAdapter::OC_ADAPTER_IP | 
681                                                         OCTransportAdapter::OC_ADAPTER_TCP);
682     cfg.QoS = OC::QualityOfService::LowQos;
683
684     OCPlatform::Configure(cfg);
685     OC_VERIFY(OCPlatform::start() == OC_STACK_OK);
686     std::cout << "Starting server & setting platform info\n";
687
688     OCStackResult result = SetPlatformInfo(gPlatformId, gManufacturerName, gManufacturerLink,
689             gModelNumber, gDateOfManufacture, gPlatformVersion, gOperatingSystemVersion,
690             gHardwareVersion, gFirmwareVersion, gSupportLink, gSystemTime);
691
692     result = OCPlatform::registerPlatformInfo(platformInfo);
693
694     if (result != OC_STACK_OK)
695     {
696         std::cout << "Platform Registration failed\n";
697         return -1;
698     }
699
700     result = SetDeviceInfo();
701
702     if (result != OC_STACK_OK)
703     {
704         std::cout << "Device Registration failed\n";
705         return -1;
706     }
707
708     try
709     {
710         // Create the instance of the resource class
711         // (in this case instance of class 'LightResource').
712         LightResource myLight;
713
714         // Invoke createResource function of class light.
715         myLight.createResource();
716         std::cout << "Created resource." << std::endl;
717
718         myLight.addType(std::string("core.brightlight"));
719         myLight.addInterface(std::string(LINK_INTERFACE));
720         std::cout << "Added Interface and Type" << std::endl;
721
722         DeletePlatformInfo();
723
724         // A condition variable will free the mutex it is given, then do a non-
725         // intensive block until 'notify' is called on it.  In this case, since we
726         // don't ever call cv.notify, this should be a non-processor intensive version
727         // of while(true);
728         std::mutex blocker;
729         std::condition_variable cv;
730         std::unique_lock<std::mutex> lock(blocker);
731         std::cout <<"Waiting" << std::endl;
732         cv.wait(lock, []{return false;});
733     }
734     catch(OCException &e)
735     {
736         std::cout << "OCException in main : " << e.what() << endl;
737     }
738
739     OC_VERIFY(OCPlatform::stop() == OC_STACK_OK);
740
741     return 0;
742 }