diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..8070c83 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,26 @@ +# Project Properties +#################### +CMAKE_MINIMUM_REQUIRED (VERSION 2.6.2) + +PROJECT(device_manager) +# This will be extended to take the version from SCM +# Version config can be simplified from 3.0+ +SET(device_manager_VERSION_MAJOR 0) +SET(device_manager_VERSION_MINOR 9) +SET(device_manager_VERSION_PATCH 0) +SET(device_manager_VERSION + ${device_manager_VERSION_MAJOR}.${device_manager_VERSION_MINOR}.${device_manager_VERSION_PATCH}) + +# Options +################### +SET(CMAKE_VERBOSE_MAKEFILE 1) +SET(CMAKE_BUILD_TYPE DEBUG) # Options MINSIZEREL, RELEASE, DEBUG +SET(DOCS_INTERNAL 1 CACHE BOOL "enable internal docs generation") + +# Includes +########## +LIST(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") + +# Paths +######## +ADD_SUBDIRECTORY(src) diff --git a/README.md b/README.md index 7ceb8ad..3550e25 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,94 @@ -# device-manager -device-manager application +## Device Manager +Device Manager application is used to provision the gateway device and constrained devices with access details to register with the FlowCloud. It runs on the gateway device and uses the Awalwm2m sdk to communicate with the awalwm2m server and client running on the same. +Currently it can be used to : +- Provision the gateway device +- Check if the gateway device is provisioned or not +- Provision the constrained device +- Check if the constrained device is provisioned or not +- List the constrained devices connected to the local server + +## Revision History +| Revision | Changes from previous revision | +| :---- | :------------------------------| +| 0.9.0 | External Beta Trial Release | + +## Prerequisites +Prior to running device manager, make sure that: +- Awalwm2m client daemon(awa_clientd) is running. +- Awalwm2m server daemon(awa_serverd) is running. + +**NOTE:** Please do "ps" on console to see "specific" process is running or not. + +## Application flow diagram +![Device Manager Sequence Diagram](docs/device-manager-ubusd-seq-diag.png) + +## Using the ubus interface of Device Manager +### Provisioning the gateway device: + +``` +root@OpenWrt:/# ubus -t 60 call device_manager provision_gateway_device '{"device_name":"MyCi40","device_type":"FlowGateway","licensee_id":7,"fcap":"XXXXXXXXXX", "licensee_secret":"XXXXXXXXXXXXXX"}' +{ + "provision_status": 0 +} +``` +Status 0 : The device was provisioned successfully. +Status 1 : Provisoning failed +Status 2 : The device was already provisioned. + +### Checking if the gateway device is provisioned or not +``` +root@OpenWrt:/# ubus call device_manager is_gateway_device_provisioned +{ + "provision_status": false +} +``` + +### Listing the devices connected to the gateway device +``` +root@OpenWrt:/# ubus call device_manager get_client_list +{ + "clients": [ + { + "clientId": "LedDevice", + "is_device_provisioned": false + } + ] +} +``` + +### Provisioning a constrained device: +``` +root@OpenWrt:/# ubus -t 60 call device_manager provision_constrained_device '{"fcap":"XXXXXXXXXX", "client_id":"LedDevice", "licensee_id": 7, "device_type" : "FlowCreatorLED", "parent_id": "08 5A 24 DE A8 C2 0A 4B AB 24 4C F6 ED 5D 5F 62 "}' +{ + "status": 0 +} +``` +**NOTE:** The parent id should be the same as the device id of the gateway device. It can be read from /etc/lwm2m/flow_access.cfg + +### Checking if the constrained device is provisioned or not +``` +root@OpenWrt:/# ubus call device_manager is_constrained_device_provisioned '{"client_id":"LedDevice"}' +{ + "provision_status": false +} + +``` + +## Debugging + +The logs of device manager application can be found at /var/log/device_manager_ubusd + +## API guide + +Device Manager documentation is available as a Doxygen presentation which is generated via the following process. + + 1. Install [Doxygen ](http://www.stack.nl/~dimitri/doxygen/download.html): ```` sudo apt-get install doxygen```` + 2. Generate the documentation: + + $ device-manager: mkdir build + $ device-manager/build: cd build + $ device-manager/build: cmake ../docs + $ device-manager/build: make docs + +The output can be found in the build/html directory and viewed by opening index.html with your web browser. +>>>>>>> dev diff --git a/docs/CMakeLists.txt b/docs/CMakeLists.txt new file mode 100644 index 0000000..221b580 --- /dev/null +++ b/docs/CMakeLists.txt @@ -0,0 +1,22 @@ +# Add docs targets +################# +CMAKE_MINIMUM_REQUIRED (VERSION 2.6.2) + +FIND_PACKAGE(Doxygen) +IF(DOXYGEN_FOUND) + SET(doxyfile_in ${CMAKE_CURRENT_SOURCE_DIR}/doxyfile.in) + + SET(doxyfile ${CMAKE_CURRENT_BINARY_DIR}/doxyfile) + CONFIGURE_FILE(${doxyfile_in} ${doxyfile} @ONLY) + + ADD_CUSTOM_TARGET(docs + COMMAND ${DOXYGEN_EXECUTABLE} ${doxyfile} + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + COMMENT "Generating API documentation with Doxygen" + VERBATIM) +ELSE() + MESSAGE(WARNING "Doxygen not found. Cannot generate documentation!") + ADD_CUSTOM_TARGET(docs + COMMENT "***Doxygen not found! Please install doxygen to make docs***" + VERBATIM) +ENDIF() diff --git a/docs/creator.css b/docs/creator.css new file mode 100644 index 0000000..8a1bbda --- /dev/null +++ b/docs/creator.css @@ -0,0 +1,16 @@ +#imglogo { + padding-left:0px; + background: url(imgteclogo.png) no-repeat right center #770172; + height:80px; +} +#creatorlogo { + background: url(creatorlogo.png) no-repeat left center transparent; + display:block; + width:260px; + height:80px; +} + +.copyright_footer { + margin-left: 10px; + color: #666666; +} diff --git a/docs/creator_footer.html b/docs/creator_footer.html new file mode 100755 index 0000000..6ee0b90 --- /dev/null +++ b/docs/creator_footer.html @@ -0,0 +1,47 @@ + + + + + + + + + + diff --git a/docs/creator_header.html b/docs/creator_header.html new file mode 100755 index 0000000..f0dc82a --- /dev/null +++ b/docs/creator_header.html @@ -0,0 +1,17 @@ + + + + +$title + + + +$search + + + + + + diff --git a/docs/creatorlogo.png b/docs/creatorlogo.png new file mode 100644 index 0000000..bfe24f1 Binary files /dev/null and b/docs/creatorlogo.png differ diff --git a/docs/device-manager-ubusd-seq-diag.png b/docs/device-manager-ubusd-seq-diag.png new file mode 100644 index 0000000..c6a3e18 Binary files /dev/null and b/docs/device-manager-ubusd-seq-diag.png differ diff --git a/docs/doxyfile.in b/docs/doxyfile.in new file mode 100644 index 0000000..3513d3f --- /dev/null +++ b/docs/doxyfile.in @@ -0,0 +1,23 @@ +PROJECT_NAME = "@CMAKE_PROJECT_NAME@" +PROJECT_NUMBER = @device_manager_VERSION@ +JAVADOC_AUTOBRIEF = YES +HTML_HEADER = @PROJECT_SOURCE_DIR@/creator_header.html +HTML_FOOTER = @PROJECT_SOURCE_DIR@/creator_footer.html +GENERATE_LATEX = NO +#HAVE_DOT = YES +STRIP_FROM_PATH = @PROJECT_SOURCE_DIR@ \ + @PROJECT_BINARY_DIR@ +INPUT = @PROJECT_SOURCE_DIR@/../src/ \ + @PROJECT_SOURCE_DIR@/mainpage.dox +FILE_PATTERNS = *.h \ + *.c +RECURSIVE = YES +EXTRACT_STATIC = YES +HTML_STYLESHEET = @PROJECT_SOURCE_DIR@/doxygen.css +HTML_EXTRA_STYLESHEET = @PROJECT_SOURCE_DIR@/creator.css +HTML_EXTRA_FILES = @PROJECT_SOURCE_DIR@/tabs.css \ + @PROJECT_SOURCE_DIR@/imgteclogo.png \ + @PROJECT_SOURCE_DIR@/creatorlogo.png +GENERATE_TAGFILE = @PROJECT_BINARY_DIR@/tagfile +USE_MDFILE_AS_MAINPAGE = @doxy_main_page@ +IMAGE_PATH = @PROJECT_SOURCE_DIR@/device-manager-ubusd-seq-diag.png diff --git a/docs/doxygen.css b/docs/doxygen.css new file mode 100644 index 0000000..9b29b38 --- /dev/null +++ b/docs/doxygen.css @@ -0,0 +1,995 @@ +/* The standard CSS for doxygen */ + +body, table, div, p, dl { + font-family: Lucida Grande, Verdana, Geneva, Arial, sans-serif; + font-size: 13px; + line-height: 1.3; +} + +#FlowHeader { + background-color:##770172; +} +#navrow1{ + background-color: ##770172; +} + +/* @group Heading Levels */ +h1 { + font-size: 150%; +} + +.title { + font-size: 150%; + font-weight: bold; + margin: 10px 2px; +} + +h2 { + font-size: 120%; +} + +h3 { + font-size: 100%; +} + +dt { + font-weight: bold; +} + +div.multicol { + -moz-column-gap: 1em; + -webkit-column-gap: 1em; + -moz-column-count: 3; + -webkit-column-count: 3; +} + +p.startli, p.startdd, p.starttd { + margin-top: 2px; +} + +p.endli { + margin-bottom: 0px; +} + +p.enddd { + margin-bottom: 4px; +} + +p.endtd { + margin-bottom: 2px; +} + +/* @end */ + +caption { + font-weight: bold; +} + +span.legend { + font-size: 70%; + text-align: center; +} + +h3.version { + font-size: 90%; + text-align: center; +} + +div.qindex, div.navtab{ + background-color: #F4EDF5; + border: 1px solid #CCAAD0; + text-align: center; +} + +div.qindex, div.navpath { + width: 100%; + line-height: 140%; +} + +div.navtab { + margin-right: 15px; +} + +/* @group Link Styling */ + +a { + color: #7C4782; + font-weight: normal; + text-decoration: none; +} + +.contents a:visited { + color: #905297; +} + +a:hover { + text-decoration: underline; +} + +a.qindex { + font-weight: bold; +} + +a.qindexHL { + font-weight: bold; + background-color: #C8A3CD; + color: #ffffff; + border: 1px double #BC8FC1; +} + +.contents a.qindexHL:visited { + color: #ffffff; +} + +a.el { + + } + +a.elRef { +} + +a.code, a.code:visited { + color: #4665A2; +} + +a.codeRef, a.codeRef:visited { + color: #4665A2; +} + +/* @end */ + +dl.el { + margin-left: -1cm; +} + +.fragment { + font-family: monospace, fixed; + font-size: 105%; +} + +pre.fragment { + border: 1px solid #DFC9E1; + background-color: #FDFBFD; + padding: 4px 6px; + margin: 4px 8px 4px 2px; + overflow: auto; + word-wrap: break-word; + font-size: 9pt; + line-height: 125%; +} + +div.ah { + background-color: black; + font-weight: bold; + color: #ffffff; + margin-bottom: 3px; + margin-top: 3px; + padding: 0.2em; + border: solid thin #333; + border-radius: 0.5em; + -webkit-border-radius: .5em; + -moz-border-radius: .5em; + box-shadow: 2px 2px 3px #999; + -webkit-box-shadow: 2px 2px 3px #999; + -moz-box-shadow: rgba(0, 0, 0, 0.15) 2px 2px 2px; + background-image: -webkit-gradient(linear, left top, left bottom, from(#eee), to(#000),color-stop(0.3, #444)); + background-image: -moz-linear-gradient(center top, #eee 0%, #444 40%, #000); +} + +div.groupHeader { + margin-left: 16px; + margin-top: 12px; + font-weight: bold; +} + +div.groupText { + margin-left: 16px; + font-style: italic; +} + +body { + background-color: white; + color: black; + margin: 0; +} + +div.contents { + margin-top: 10px; + margin-left: 8px; + margin-right: 8px; +} + +td.indexkey { + background-color: #F4EDF5; + font-weight: bold; + border: 1px solid #DFC9E1; + margin: 2px 0px 2px 0; + padding: 2px 10px; + white-space: nowrap; + vertical-align: top; +} + +td.indexvalue { + background-color: #F4EDF5; + border: 1px solid #DFC9E1; + padding: 2px 10px; + margin: 2px 0px; +} + +tr.memlist { + background-color: #F5EFF6; +} + +p.formulaDsp { + text-align: center; +} + +img.formulaDsp { + +} + +img.formulaInl { + vertical-align: middle; +} + +div.center { + text-align: center; + margin-top: 0px; + margin-bottom: 0px; + padding: 0px; +} + +div.center img { + border: 0px; +} + +address.footer { + text-align: right; + padding-right: 12px; +} + +img.footer { + border: 0px; + vertical-align: middle; +} + +/* @group Code Colorization */ + +span.keyword { + color: #008000 +} + +span.keywordtype { + color: #604020 +} + +span.keywordflow { + color: #e08000 +} + +span.comment { + color: #800000 +} + +span.preprocessor { + color: #806020 +} + +span.stringliteral { + color: #002080 +} + +span.charliteral { + color: #008080 +} + +span.vhdldigit { + color: #ff00ff +} + +span.vhdlchar { + color: #000000 +} + +span.vhdlkeyword { + color: #700070 +} + +span.vhdllogic { + color: #ff0000 +} + +/* @end */ + +td.tiny { + font-size: 75%; +} + +.dirtab { + padding: 4px; + border-collapse: collapse; + border: 1px solid #CCAAD0; +} + +th.dirtab { + background: #F4EDF5; + font-weight: bold; +} + +hr { + height: 0px; + border: none; + border-top: 1px solid #97569E; +} + +hr.footer { + height: 1px; +} + +/* @group Member Descriptions */ + +table.memberdecls { + border-spacing: 0px; + padding: 0px; +} + +.mdescLeft, .mdescRight, +.memItemLeft, .memItemRight, +.memTemplItemLeft, .memTemplItemRight, .memTemplParams { + background-color: #FBF9FC; + border: none; + margin: 4px; + padding: 1px 0 0 8px; +} + +.mdescLeft, .mdescRight { + padding: 0px 8px 4px 8px; + color: #555; +} + +.memItemLeft, .memItemRight, .memTemplParams { + border-top: 1px solid #DFC9E1; +} + +.memItemLeft, .memTemplItemLeft { + white-space: nowrap; +} + +.memItemRight { + width: 100%; +} + +.memTemplParams { + color: #905297; + white-space: nowrap; +} + +/* @end */ + +/* @group Member Details */ + +/* Styles for detailed member documentation */ + +.memtemplate { + font-size: 80%; + color: #905297; + font-weight: normal; + margin-left: 9px; +} + +.memnav { + background-color: #F4EDF5; + border: 1px solid #CCAAD0; + text-align: center; + margin: 2px; + margin-right: 15px; + padding: 2px; +} + +.mempage { + width: 100%; +} + +.memitem { + padding: 0; + margin-bottom: 10px; + margin-right: 5px; +} + +.memname { + white-space: nowrap; + font-weight: bold; + margin-left: 6px; +} + +.memproto, dl.reflist dt { + border-top: 1px solid #CFAED3; + border-left: 1px solid #CFAED3; + border-right: 1px solid #CFAED3; + padding: 6px 0px 6px 0px; + color: #4B2B4F; + font-weight: bold; + text-shadow: 0px 1px 1px rgba(255, 255, 255, 0.9); + /* opera specific markup */ + box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.15); + border-top-right-radius: 8px; + border-top-left-radius: 8px; + /* firefox specific markup */ + -moz-box-shadow: rgba(0, 0, 0, 0.15) 5px 5px 5px; + -moz-border-radius-topright: 8px; + -moz-border-radius-topleft: 8px; + /* webkit specific markup */ + -webkit-box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.15); + -webkit-border-top-right-radius: 8px; + -webkit-border-top-left-radius: 8px; + background-repeat:repeat-x; + background-color: #EFE4F0; + +} + +.memdoc, dl.reflist dd { + border-bottom: 1px solid #CFAED3; + border-left: 1px solid #CFAED3; + border-right: 1px solid #CFAED3; + padding: 2px 5px; + background-color: #FDFBFD; + border-top-width: 0; + /* opera specific markup */ + border-bottom-left-radius: 8px; + border-bottom-right-radius: 8px; + box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.15); + /* firefox specific markup */ + -moz-border-radius-bottomleft: 8px; + -moz-border-radius-bottomright: 8px; + -moz-box-shadow: rgba(0, 0, 0, 0.15) 5px 5px 5px; + background-image: -moz-linear-gradient(center top, #FFFFFF 0%, #FFFFFF 60%, #FAF7FB 95%, #F5EFF6); + /* webkit specific markup */ + -webkit-border-bottom-left-radius: 8px; + -webkit-border-bottom-right-radius: 8px; + -webkit-box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.15); + background-image: -webkit-gradient(linear,center top,center bottom,from(#FFFFFF), color-stop(0.6,#FFFFFF), color-stop(0.60,#FFFFFF), color-stop(0.95,#FAF7FB), to(#F5EFF6)); +} + +dl.reflist dt { + padding: 5px; +} + +dl.reflist dd { + margin: 0px 0px 10px 0px; + padding: 5px; +} + +.paramkey { + text-align: right; +} + +.paramtype { + white-space: nowrap; +} + +.paramname { + color: #602020; + white-space: nowrap; +} +.paramname em { + font-style: normal; +} + +.params, .retval, .exception, .tparams { + border-spacing: 6px 2px; +} + +.params .paramname, .retval .paramname { + font-weight: bold; + vertical-align: top; +} + +.params .paramtype { + font-style: italic; + vertical-align: top; +} + +.params .paramdir { + font-family: "courier new",courier,monospace; + vertical-align: top; +} + +table.mlabels { + border-spacing: 0px; +} + +td.mlabels-left { + width: 100%; + padding: 0px; +} + +td.mlabels-right { + vertical-align: bottom; + padding: 0px; + white-space: nowrap; +} + +span.mlabels { + margin-left: 8px; +} + +span.mlabel { + background-color: #728DC1; + border-top:1px solid #5373B4; + border-left:1px solid #5373B4; + border-right:1px solid #C4CFE5; + border-bottom:1px solid #C4CFE5; + text-shadow: none; + color: white; + margin-right: 4px; + padding: 2px 3px; + border-radius: 3px; + font-size: 7pt; + white-space: nowrap; + vertical-align: middle; +} + + +/* @end */ + +/* @group Directory (tree) */ + +/* for the tree view */ + +.ftvtree { + font-family: sans-serif; + margin: 0px; +} + +/* these are for tree view when used as main index */ + +.directory { + font-size: 9pt; + font-weight: bold; + margin: 5px; +} + +.directory h3 { + margin: 0px; + margin-top: 1em; + font-size: 11pt; +} + +.directory > h3 { + margin-top: 0; +} + +.directory p { + margin: 0px; + white-space: nowrap; +} + +.directory div { + display: none; + margin: 0px; +} + +.directory img { + vertical-align: -30%; +} + +/* these are for tree view when not used as main index */ + +.directory-alt { + font-size: 100%; + font-weight: bold; +} + +.directory-alt h3 { + margin: 0px; + margin-top: 1em; + font-size: 11pt; +} + +.directory-alt > h3 { + margin-top: 0; +} + +.directory-alt p { + margin: 0px; + white-space: nowrap; +} + +.directory-alt div { + display: none; + margin: 0px; +} + +.directory-alt img { + vertical-align: -30%; +} + +/* @end */ + +div.dynheader { + margin-top: 8px; +} + +address { + font-style: normal; + color: #56315B; +} + +table.doxtable { + border-collapse:collapse; + margin-left: 10px; +} + +table.doxtable td, table.doxtable th { + border: 1px solid #5C3460; + padding: 3px 7px 2px; +} + +table.doxtable th { + background-color: #714076; + color: #FFFFFF; + font-size: 110%; + padding-bottom: 4px; + padding-top: 5px; + text-align:left; +} + +table.fieldtable { + width: 100%; + margin-bottom: 10px; + border: 1px solid #CFAED3; + border-spacing: 0px; + -moz-border-radius: 4px; + -webkit-border-radius: 4px; + border-radius: 4px; + -moz-box-shadow: rgba(0, 0, 0, 0.15) 2px 2px 2px; + -webkit-box-shadow: 2px 2px 2px rgba(0, 0, 0, 0.15); + box-shadow: 2px 2px 2px rgba(0, 0, 0, 0.15); +} + +.fieldtable td, .fieldtable th { + padding: 3px 7px 2px; +} + +.fieldtable td.fieldtype, .fieldtable td.fieldname { + white-space: nowrap; + border-right: 1px solid #CFAED3; + border-bottom: 1px solid #CFAED3; + vertical-align: top; +} + +.fieldtable td.fielddoc { + border-bottom: 1px solid #CFAED3; + width: 100%; +} + +.fieldtable tr:last-child td { + border-bottom: none; +} + +.fieldtable th { + background-repeat:repeat-x; + background-color: #EFE4F0; + font-size: 90%; + color: #4B2B4F; + padding-bottom: 4px; + padding-top: 5px; + text-align:left; + -moz-border-radius-topleft: 4px; + -moz-border-radius-topright: 4px; + -webkit-border-top-left-radius: 4px; + -webkit-border-top-right-radius: 4px; + border-top-left-radius: 4px; + border-top-right-radius: 4px; + border-bottom: 1px solid #CFAED3; +} + + +.tabsearch { + top: 0px; + left: 10px; + height: 36px; + z-index: 101; + overflow: hidden; + font-size: 13px; +} + +.navpath ul +{ + font-size: 11px; + background-repeat:repeat-x; + height:30px; + line-height:30px; + color:#BE92C3; + border:solid 1px #DDC6E0; + overflow:hidden; + margin:0px; + padding:0px; +} + +.navpath li +{ + list-style-type:none; + float:left; + padding-left:10px; + padding-right:15px; + background-repeat:no-repeat; + background-position:right; + color:#6E3F73; +} + +.navpath li.navelem a +{ + height:32px; + display:block; + text-decoration: none; + outline: none; +} + +.navpath li.navelem a:hover +{ + color:#AC73B2; +} + +.navpath li.footer +{ + list-style-type:none; + float:right; + padding-left:10px; + padding-right:15px; + background-image:none; + background-repeat:no-repeat; + background-position:right; + color:#6E3F73; + font-size: 8pt; +} + + +div.summary +{ + float: right; + font-size: 8pt; + padding-right: 5px; + width: 50%; + text-align: right; +} + +div.summary a +{ + white-space: nowrap; +} + +div.ingroups +{ + margin-left: 5px; + font-size: 8pt; + padding-left: 5px; + width: 50%; + text-align: left; +} + +div.ingroups a +{ + white-space: nowrap; +} + +div.header +{ + background-repeat:repeat-x; + background-color: #FBF9FC; + margin: 0px; + border-bottom: 1px solid #DFC9E1; +} + + +div.line { + font-family: monospace, fixed; + font-size: 13px; + min-height: 13px; + line-height: 1.0; + text-wrap: unrestricted; + white-space: -moz-pre-wrap; /* Moz */ + white-space: -pre-wrap; /* Opera 4-6 */ + white-space: -o-pre-wrap; /* Opera 7 */ + white-space: pre-wrap; /* CSS3 */ + word-wrap: break-word; /* IE 5.5+ */ + text-indent: -53px; + padding-left: 53px; + padding-bottom: 0px; + margin: 0px; + -webkit-transition-property: background-color, box-shadow; + -webkit-transition-duration: 0.5s; + -moz-transition-property: background-color, box-shadow; + -moz-transition-duration: 0.5s; + -ms-transition-property: background-color, box-shadow; + -ms-transition-duration: 0.5s; + -o-transition-property: background-color, box-shadow; + -o-transition-duration: 0.5s; + transition-property: background-color, box-shadow; + transition-duration: 0.5s; +} + + +div.headertitle +{ + padding: 5px 5px 5px 7px; +} + +dl +{ + padding: 0 0 0 10px; +} + +dl.note, dl.warning, dl.attention, dl.pre, dl.post, dl.invariant, dl.deprecated, dl.todo, dl.test, dl.bug +{ + border-left:4px solid; + padding: 0 0 0 6px; +} + +dl.note +{ + border-color: #D0C000; +} + +dl.warning, dl.attention +{ + border-color: #FF0000; +} + +dl.pre, dl.post, dl.invariant +{ + border-color: #00D000; +} + +dl.deprecated +{ + border-color: #505050; +} + +dl.todo +{ + border-color: #00C0E0; +} + +dl.test +{ + border-color: #3030E0; +} + +dl.bug +{ + border-color: #C08050; +} + +#projectlogo +{ + text-align: center; + vertical-align: bottom; + border-collapse: separate; +} + +#projectlogo img +{ + border: 0px none; +} + +#projectname +{ + font: 300% Tahoma, Arial,sans-serif; + margin: 0px; + padding: 2px 0px; +} + +#projectbrief +{ + font: 120% Tahoma, Arial,sans-serif; + margin: 0px; + padding: 0px; +} + +#projectnumber +{ + font: 50% Tahoma, Arial,sans-serif; + margin: 0px; + padding: 0px; +} + +#titlearea +{ + padding: 0px; + margin: 0px; + width: 100%; + border-bottom: 1px solid #A05FA8; +} + +.image +{ + text-align: center; +} + +.dotgraph +{ + text-align: center; +} + +.mscgraph +{ + text-align: center; +} + +.caption +{ + font-weight: bold; +} + +div.zoom +{ + border: 1px solid #C298C6; +} + +dl.citelist { + margin-bottom:50px; +} + +dl.citelist dt { + color:#683B6D; + float:left; + font-weight:bold; + margin-right:10px; + padding:5px; +} + +dl.citelist dd { + margin:2px 0; + padding:5px 0; +} + +@media print +{ + #top { display: none; } + #side-nav { display: none; } + #nav-path { display: none; } + body { overflow:visible; } + h1, h2, h3, h4, h5, h6 { page-break-after: avoid; } + .summary { display: none; } + .memitem { page-break-inside: avoid; } + #doc-content + { + margin-left:0 !important; + height:auto !important; + width:auto !important; + overflow:inherit; + display:inline; + } + pre.fragment + { + overflow: visible; + text-wrap: unrestricted; + white-space: -moz-pre-wrap; /* Moz */ + white-space: -pre-wrap; /* Opera 4-6 */ + white-space: -o-pre-wrap; /* Opera 7 */ + white-space: pre-wrap; /* CSS3 */ + word-wrap: break-word; /* IE 5.5+ */ + } +} + +.icon { + font-family: Arial, Helvetica; + font-weight: bold; + font-size: 12px; + height: 14px; + width: 16px; + display: inline-block; + background-color: #B17CB7; + color: white; + text-align: center; + border-radius: 4px; + margin-left: 2px; + margin-right: 2px; +} + +.icona { + width: 24px; + height: 22px; + display: inline-block; +} diff --git a/docs/imgteclogo.png b/docs/imgteclogo.png new file mode 100644 index 0000000..8053553 Binary files /dev/null and b/docs/imgteclogo.png differ diff --git a/docs/mainpage.dox b/docs/mainpage.dox new file mode 100644 index 0000000..b4e57a0 --- /dev/null +++ b/docs/mainpage.dox @@ -0,0 +1,12 @@ +/** @mainpage Flow Device Manager +@section overview Overview + +Device Manager application is used to provision the gateway device and constrained devices with access details to register with the FlowCloud. It runs on the gateway device and uses the Awalwm2m sdk to communicate with the awalwm2m server and client running on the same. Currently it can be used to : + +- Provision the gateway device +- Check if the gateway device is provisioned or not +- Provision the constrained device +- Check if the constrained device is provisioned or not +- List the constrained devices connected to the local server + + */ diff --git a/docs/tabs.css b/docs/tabs.css new file mode 100644 index 0000000..bd7290f --- /dev/null +++ b/docs/tabs.css @@ -0,0 +1,55 @@ +.tabs, .tabs2, .tabs3 { + background-color: #511557; + width: 100%; + z-index: 101; + font-size: 13px; +} + +.tabs2 { + font-size: 10px; +} +.tabs3 { + font-size: 9px; +} + +.tablist { + margin: 0; + padding: 0; + display: table; +} + +.tablist li { + float: left; + display: table-cell; + line-height: 36px; + list-style: none; +} + +.tablist a { + display: block; + padding: 0 20px; + font-weight: bold; + background-repeat:no-repeat; + background-position:right; + color: #FFFFFF; + text-shadow: none + text-decoration: none; + outline: none; +} + +.tabs3 .tablist a { + padding: 0 10px; +} + +.tablist a:hover { + background-color: #DFDFDF; + color: #770172; + text-decoration: none; + text-shadow: none; +} + +.tablist li.current a { + background-color: #FFFFFF; + color: #770172; + text-shadow: none; +} diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt new file mode 100644 index 0000000..e390569 --- /dev/null +++ b/src/CMakeLists.txt @@ -0,0 +1,22 @@ +# Add library targets +##################### +SET(SOURCES device_manager.c fdm_register.c fdm_subscribe.c fdm_licensee_verification.c + fdm_hmac.c fdm_server_session.c fdm_get_client_list.c fdm_provision_constrained.c) +ADD_LIBRARY(devicemanager SHARED ${SOURCES}) + +INCLUDE(FindPkgConfig) +PKG_CHECK_MODULES(JSON ${STRICT_CHECK} json-c) +INCLUDE_DIRECTORIES(${JSON_INCLUDE_DIRS}) + +FIND_LIBRARY(LIB_AWA libawa.so PATHS ${STAGING_DIR}/usr/lib) +TARGET_LINK_LIBRARIES(devicemanager ${LIB_AWA} json-c blobmsg_json) + +# Add executable targets +######################## +ADD_EXECUTABLE(device_manager_ubusd device_manager_ubus.c) +TARGET_LINK_LIBRARIES(device_manager_ubusd devicemanager ubus ubox json-c blobmsg_json) + +# Add install targets +###################### +INSTALL(TARGETS devicemanager LIBRARY DESTINATION lib) +INSTALL(TARGETS device_manager_ubusd RUNTIME DESTINATION bin) diff --git a/src/device_manager.c b/src/device_manager.c new file mode 100644 index 0000000..89e77fc --- /dev/null +++ b/src/device_manager.c @@ -0,0 +1,370 @@ +/*************************************************************************************************** + * Copyright (c) 2016, Imagination Technologies Limited and/or its affiliated group companies + * and/or licensors + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used to + * endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY + * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @file device_manager.c + * @brief Provides provisioning operations. + */ + +/*************************************************************************************************** + * Includes + **************************************************************************************************/ + +#include +#include +#include +#include +#include "awa/common.h" +#include "awa/client.h" +#include "device_manager.h" +#include "fdm_register.h" +#include "fdm_subscribe.h" +#include "fdm_licensee_verification.h" +#include "fdm_common.h" +#include "fdm_log.h" + +/*************************************************************************************************** + * Macros + **************************************************************************************************/ + +//! \{ +#define IPC_PORT (12345) +#define IPC_ADDRESS "127.0.0.1" +#define SERVER_RESPONSE_TIMEOUT (30) +#define MAX_STRINGS (15) +#define FLOW_ACCESS_CFG "/etc/lwm2m/flow_access.cfg" +//! \} + +/*************************************************************************************************** + * Globals + **************************************************************************************************/ + +/** + * Flow object to hold Flow Cloud specific information. + */ +const OBJECT_T flowObject = +{ "FlowObject", Lwm2mObjectId_FlowObject, 11, + (RESOURCE_T []) + { + { FlowObjectResourceId_DeviceId, "DeviceID", AwaResourceType_Opaque, true, true }, + { FlowObjectResourceId_ParentId, "ParentID", AwaResourceType_Opaque, false, false }, + { FlowObjectResourceId_DeviceType, "DeviceType", AwaResourceType_String, true, true }, + { FlowObjectResourceId_DeviceName, "Name", AwaResourceType_String, false, false }, + { FlowObjectResourceId_Description, "Description", AwaResourceType_String, false, false }, + { FlowObjectResourceId_Fcap, "FCAP", AwaResourceType_String, true, true }, + { FlowObjectResourceId_LicenseeId, "LicenseeID", AwaResourceType_Integer, true, true }, + { FlowObjectResourceId_LicenseeChallenge, "LicenseeChallenge", AwaResourceType_Opaque, false, false }, + { FlowObjectResourceId_HashIterations, "HashIterations", AwaResourceType_Integer, false, false }, + { FlowObjectResourceId_LicenseeHash, "LicenseeHash", AwaResourceType_Opaque, false, false }, + { FlowObjectResourceId_Status, "Status", AwaResourceType_Integer, false, false } + } +}; + +/** + * Flow Access object to hold information for accessing Flow Cloud. + */ +const OBJECT_T flowAccessObject = +{ "FlowAccess", Lwm2mObjectId_FlowAccess, 5, + (RESOURCE_T []) + { + { FlowAccessResourceId_Url, "URL", AwaResourceType_String, false, true }, + { FlowAccessResourceId_CustomerKey, "CustomerKey", AwaResourceType_String, false, true }, + { FlowAccessResourceId_CustomerSecret, "CustomerSecret", AwaResourceType_String, false, true }, + { FlowAccessResourceId_RememberMeToken, "RememberMeToken", AwaResourceType_String, false, true }, + { FlowAccessResourceId_RememberMeTokenExpiry, "RememberMeTokenExpiry", AwaResourceType_Time, false, true } + } +}; + +/** + * Initialise Awa standard objects. + * Device object: hold device specific information. + */ +static const OBJECT_T deviceObject = +{ "DeviceObject", Lwm2mObjectId_DeviceObject, 2, + (RESOURCE_T []) + { + { DeviceObjectResourceId_SerialNumber, "SerialNumber", AwaResourceType_String, false, true }, + { DeviceObjectResourceId_SoftwareVersion, "SoftwareVersion", AwaResourceType_String, false, true } + } +}; + +/** + * A Session is required for interaction with the Awa LWM2M Core. + * Operations to interact with Core resources are created in the context of a session. + * The session is owned by the caller and should eventually be freed with AwaClientSession_Free. + */ +static AwaClientSession *session = NULL; + +int debugLevel = LOG_INFO; +FILE *debugStream = NULL; + +/*************************************************************************************************** + * Methods + **************************************************************************************************/ + +FILE* SetLogFile(const char *file) +{ + FILE *logFile = NULL; + logFile = fopen(file, "w"); + if (logFile != NULL) + { + LOG(LOG_DBG, "Log file set to %s", file); + debugStream = logFile; + } + else + { + LOG(LOG_ERR, "Failed to create or open %s file", file); + } + return logFile; +} + +void SetDebugLevel(unsigned int level) +{ + LOG(LOG_DBG, "Set debug level to %u", level); + debugLevel = level; +} + +bool EstablishSession(void) +{ + AwaError error; + bool result = false; + + LOG(LOG_INFO, "Establish session with lwm2m client"); + + session = AwaClientSession_New(); + if (session == NULL) + { + LOG(LOG_ERR, "Failed to create session"); + return false; + } + + if ((error = AwaClientSession_SetIPCAsUDP(session, IPC_ADDRESS, IPC_PORT)) == AwaError_Success) + { + if ((error = AwaClientSession_Connect(session)) == AwaError_Success) + { + result = true; + } + else + { + LOG(LOG_ERR, "Failed to connect session with lwm2m client\nerror: %s", AwaError_ToString(error)); + } + } + else + { + LOG(LOG_ERR, "Failed to set IPC as UDP\nerror: %s", AwaError_ToString(error)); + } + return result; +} + +/** + * @brief Save details which are required to access flow cloud or required by flow_button_gateway + * application. This will save value of those resources for which wantToSave member is set. + * @return true for success otherwise false. + */ +static bool SaveFlowCloudAccessDetails() +{ + char strings[MAX_STRINGS][MAX_STR_SIZE] = {{0}}; + unsigned int i, resCount = 0; + OBJECT_T objects[] = + { + flowObject, + flowAccessObject, + deviceObject + }; + LOG(LOG_INFO, "Saving flow cloud access details..."); + + if (!(resCount = GetResources(session, objects, ARRAY_SIZE(objects), strings))) + { + LOG(LOG_ERR, "Failed to get objects resource values"); + return false; + } + + FILE *configFile = fopen(FLOW_ACCESS_CFG, "w"); + if (configFile == NULL) + { + LOG(LOG_ERR, "Failed to create or open "FLOW_ACCESS_CFG); + return false; + } + for (i = 0; i < resCount; i++) + { + fprintf(configFile, "%s\n", strings[i]); + } + if (fclose(configFile)) + { + LOG(LOG_ERR, "Failed to close "FLOW_ACCESS_CFG); + } + return true; +} + +ProvisionStatus ProvisionGatewayDevice(const char *deviceName, const char *deviceType, int licenseeID, const char *fcap, const char *licenseeSecret) +{ + FlowSubscriptions subscriptions = {0}; + Verification verificationData; + unsigned int timeout = SERVER_RESPONSE_TIMEOUT; + unsigned int sleepTime = SLEEP_COUNT; + OBJECT_T flowObjects[] = + { + flowObject, + flowAccessObject, + }; + + if (deviceName == NULL || deviceType == NULL || fcap == NULL || licenseeSecret == NULL) + { + LOG(LOG_ERR, "Null parameters passed to %s()", __func__); + return PROVISION_FAIL; + } + + LOG(LOG_INFO, "Provisioning device with following details:\n" + "\n%-15s\t = %s\n%-15s\t = %s\n%-15s\t = %d\n%-15s\t = %s\n%-15s\t = %s", "Device Name", + deviceName, "Device Type", deviceType, "Licensee ID", licenseeID, "FCAP", fcap, + "Licensee Secret", licenseeSecret); + + if (!DefineObjectsAtClient(session, flowObjects, ARRAY_SIZE(flowObjects))) + { + LOG(LOG_ERR, "Failed to define Flow objects"); + return PROVISION_FAIL; + } + + if(IsGatewayDeviceProvisioned()) + { + return ALREADY_PROVISIONED; + } + + if (!PopulateFlowObject(session, deviceName, deviceType, licenseeID, fcap)) + { + LOG(LOG_ERR, "Failed to populate flow object with device type, licensee id and fcap"); + return PROVISION_FAIL; + } + + memset(&verificationData, 0, sizeof(Verification)); + verificationData.waitForServerResponse = true; + verificationData.hasChallenge = false; + verificationData.hasIterations = false; + verificationData.verifyLicensee = false; + verificationData.isProvisionSuccess = false; + + if (!SubscribeToFlowObjects(session, &subscriptions, &verificationData)) + { + LOG(LOG_ERR, "Failed to subscribe flow and flow access objects"); + return PROVISION_FAIL; + } + + LOG(LOG_INFO, "Waiting for responses from FlowCloud server..."); + + while(verificationData.waitForServerResponse && --timeout) + { + AwaClientSession_Process(session, IPC_TIMEOUT); + AwaClientSession_DispatchCallbacks(session); + + if (verificationData.verifyLicensee) + { + if (PerformFlowLicenseeVerification(session, &verificationData, licenseeSecret)) + { + verificationData.verifyLicensee = false; + } + } + sleep(1); + } + + if (!timeout) + { + LOG(LOG_ERR, "No response within timeout"); + } + + // FIXME: Temporary code until status resource is removed from FlowObject so we only get one + // change notification before canceling the subscription. + // Right now Message IDs aren't used in IPC, so it is possible to get messages out of order, + // causing the application to parse a response it is not expecting. + LOG(LOG_INFO, "Waiting for any residual notifications..."); + while(sleepTime-- > 0) + { + AwaClientSession_Process(session, IPC_TIMEOUT); + AwaClientSession_DispatchCallbacks(session); + sleep(1); + } + + // Clean up + UnSubscribeFromFlowObjects(session, &subscriptions); + if (verificationData.challenge.Data != NULL) + { + free(verificationData.challenge.Data); + } + + if (verificationData.licenseeHash.Data != NULL) + { + free(verificationData.licenseeHash.Data); + } + + if (!verificationData.isProvisionSuccess) + { + return PROVISION_FAIL; + } + + if (!SaveFlowCloudAccessDetails()) + { + LOG(LOG_ERR, "Failed to save flow cloud access details"); + } + return PROVISION_OK; +} + +bool IsGatewayDeviceProvisioned(void) +{ + LOG(LOG_INFO, "Checking whether Gateway device is provisioned"); + if (DoesObjectExist(session, Lwm2mObjectId_FlowAccess, OBJECT_INSTANCE_ID)) + { + LOG(LOG_INFO, "Provisioned"); + return true; + } + else + { + LOG(LOG_INFO, "Not Provisioned"); + return false; + } +} + +void ReleaseSession() +{ + LOG(LOG_INFO, "Disconnecting session with lwm2m client"); + + if (session == NULL) + { + return; + } + + if (AwaClientSession_Disconnect(session) != AwaError_Success) + { + LOG(LOG_ERR, "Failed to disconnect session"); + } + + if (AwaClientSession_Free(&session) != AwaError_Success) + { + LOG(LOG_WARN, "Failed to free session"); + } +} diff --git a/src/device_manager.h b/src/device_manager.h new file mode 100644 index 0000000..53d783d --- /dev/null +++ b/src/device_manager.h @@ -0,0 +1,143 @@ +/*************************************************************************************************** + * Copyright (c) 2016, Imagination Technologies Limited and/or its affiliated group companies + * and/or licensors + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used to + * endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY + * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @file device_manager.h + * @brief Header file for exposing device manager functions. + */ + +#ifndef DEVICE_MANAGER_H +#define DEVICE_MANAGER_H + +#include +#include +#include "fdm_common.h" + +//! \{ +#define MAX_STR_SIZE (64) +#define DEFAULT_PROVSIONING_TIMEOUT (30) +//! \} + +/** + * Provision status enum. + */ +typedef enum +{ + PROVISION_OK, + PROVISION_FAIL, + ALREADY_PROVISIONED, +}ProvisionStatus; + +/** + * @brief Provisioning details. + */ +typedef struct +{ + //! \{ + char deviceType[MAX_STR_SIZE]; + int licenseeID; + char fcap[MAX_STR_SIZE]; + //! \} +}ProvisioningInfo; + +/** + * @brief Set log file to dump logs. + * @param[in] file Log file. + */ +FILE* SetLogFile(const char *file); + +/** + * @brief Set debug level. + * @param[in] level debug level. + */ +void SetDebugLevel(unsigned int level); + +/** + * @brief Establish a session, configure the IPC mechanism and connect the session to Awa LWM2M + * Core. + * @return true for success otherwise false. + */ +bool EstablishSession(); + +/** + * @brief Provision Gateway device to access Flow Cloud. + * @param[in] deviceName User assigned name of device. + * @param[in] deviceType FlowCloud registered device type. + * @param[in] licenseeID Licensee. + * @param[in] fcap FlowCloud Access Provisioning Code. + * @param[in] licenseeSecret Licensee Secret. + * @return 0 for PROVISION_OK + 1 for PROVISION_FAIL + 2 for ALREADY_PROVISIONED + */ +ProvisionStatus ProvisionGatewayDevice(const char *deviceName, const char *deviceType, + int licenseeID, const char *fcap, const char *licenseeSecret); + +/** + * @brief Check whether gateway device is already provisioned or not. + * @return true for success otherwise false. + */ +bool IsGatewayDeviceProvisioned(); + +/** + * @brief Disconnect session from the Awa LWM2M Core and shut down the session, free up any + * allocated memory. + */ +void ReleaseSession(); + +/** + * @brief Get list of clients registered on Awa LWM2M server. + * @param[out] respObj Json object to be filled with list of clients. + */ +void GetClientList(json_object *respObj); + +/** + * @brief Provision a Constrained Device that has connected to the Gateway with FlowCloud + * @param[in] clientID User assigned name of device + * @param[in] fcap FCAP code + * @param[in] deviceType registered device type + * @param[in] licenseeID Licensee ID. + * @param[in] parentID Device ID of Gateway device. + * @param[in] timeout Time to wait for provisioning to complete. + * @return 0 for PROVISION_OK + 1 for PROVISION_FAIL + 2 for ALREADY_PROVISIONED + */ +ProvisionStatus ProvisionConstrainedDevice(const char *clientID, const char *fcap, + const char *deviceType, int licenseeID, const char *parentID, int timeout); + +/** + * @brief Check if constrained device is provisioned or not + * @param[in] clientID User assigned name of device + * @return true if device is present and provisioned, false otherwise + */ +bool IsConstrainedDeviceProvisioned(const char* clientID); + +#endif /* DEVICE_MANAGER_H */ diff --git a/src/device_manager_ubus.c b/src/device_manager_ubus.c new file mode 100644 index 0000000..45e4836 --- /dev/null +++ b/src/device_manager_ubus.c @@ -0,0 +1,367 @@ +/*************************************************************************************************** + * Copyright (c) 2016, Imagination Technologies Limited and/or its affiliated group companies + * and/or licensors + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used to + * endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY + * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @file device_manager_ubus.c + * @brief Provides provisioning operations. + */ + +/*************************************************************************************************** + * Includes + **************************************************************************************************/ + +#include +#include +#include +#include + +#include "device_manager.h" +#include "fdm_log.h" + +/*************************************************************************************************** + * Enums + **************************************************************************************************/ + +/** + * Provision device arguments enum. + */ +enum { + ARG_DEVICE_NAME, + ARG_DEVICE_TYPE, + ARG_LICENSEE_ID, + ARG_FCAP, + ARG_LICENSEE_SECRET, + PROVISION_GATEWAY_DEVICE_MAX +}; + +enum { + ARG_CONSTRAINED_CLIENT_ID, + ARG_CONSTRAINED_DEVICE_TYPE, + ARG_CONSTRAINED_LICENSEE_ID, + ARG_CONSTRAINED_FCAP, + ARG_CONSTRAINED_PARENT_ID, + ARG_CONSTRAINED_TIMEOUT, + PROVISION_CONSTRAINED_DEVICE_MAX +}; + +enum { + ARG_CLIENT_ID, + IS_CONSTRAINED_DEVICE_PROVISIONED_MAX +}; + +/*************************************************************************************************** + * Typedefs + **************************************************************************************************/ + +/** + * A structure to store command line arguments. + */ +typedef struct +{ + //! \{ + const char *logFile; + unsigned int debugLevel; + //! \} +} CmdOpts; + +/*************************************************************************************************** + * Globals + **************************************************************************************************/ + +/** Provision gateway device arguments and their type. */ +static const struct blobmsg_policy provisionGatewayDevicePolicy[PROVISION_GATEWAY_DEVICE_MAX] = +{ + [ARG_DEVICE_NAME] = {.name = "device_name", .type = BLOBMSG_TYPE_STRING}, + [ARG_DEVICE_TYPE] = {.name = "device_type", .type = BLOBMSG_TYPE_STRING}, + [ARG_LICENSEE_ID] = {.name = "licensee_id", .type = BLOBMSG_TYPE_INT32}, + [ARG_FCAP] = {.name = "fcap", .type = BLOBMSG_TYPE_STRING}, + [ARG_LICENSEE_SECRET] = {.name = "licensee_secret", .type = BLOBMSG_TYPE_STRING}, +}; + +/** Provision constrained device arguments and their type. */ +static const struct blobmsg_policy + provisionConstrainedDevicePolicy[PROVISION_CONSTRAINED_DEVICE_MAX] = +{ + [ARG_CONSTRAINED_CLIENT_ID] = {.name = "client_id", .type = BLOBMSG_TYPE_STRING}, + [ARG_CONSTRAINED_DEVICE_TYPE] = {.name = "device_type", .type = BLOBMSG_TYPE_STRING}, + [ARG_CONSTRAINED_LICENSEE_ID] = {.name = "licensee_id", .type = BLOBMSG_TYPE_INT32}, + [ARG_CONSTRAINED_FCAP] = {.name = "fcap", .type = BLOBMSG_TYPE_STRING}, + [ARG_CONSTRAINED_PARENT_ID] = {.name = "parent_id", .type = BLOBMSG_TYPE_STRING}, + [ARG_CONSTRAINED_TIMEOUT] = {.name = "timeout", .type = BLOBMSG_TYPE_INT32} +}; + +/** IsConstrainedDeviceProvisioned arguments and their type. */ +static const struct blobmsg_policy + isConstrainedDeviceProvisionedPolicy[IS_CONSTRAINED_DEVICE_PROVISIONED_MAX] = +{ + [ARG_CLIENT_ID] = {.name = "client_id", .type = BLOBMSG_TYPE_STRING}, +}; + +/*************************************************************************************************** + * Methods + **************************************************************************************************/ + +//! \{ +static void PrintUsage(const char *program) +{ + printf("Usage: %s [options]\n\n" + " -l : Log filename\n" + " -v : Debug level from 1 to 5\n" + " fatal(1), error(2), warning(3), info(4), debug(5)\n" + " default is info\n" + " -h : Print help and exit\n\n", + program); +} + +static int ParseCommandArgs(int argc, char *argv[], CmdOpts *cmdOpts) +{ + int opt, tmp; + opterr = 0; + + /* default values */ + cmdOpts->logFile = NULL; + cmdOpts->debugLevel = LOG_INFO; + + while (1) + { + opt = getopt(argc, argv, "l:v:"); + if (opt == -1) + { + break; + } + + switch (opt) + { + case 'l': + cmdOpts->logFile = optarg; + break; + case 'v': + tmp = strtoul(optarg, NULL, 0); + if (tmp >= LOG_FATAL && tmp <= LOG_DBG) + { + cmdOpts->debugLevel = tmp; + } + else + { + LOG(LOG_ERR, "Invalid debug level"); + PrintUsage(argv[0]); + return -1; + } + break; + case 'h': + PrintUsage(argv[0]); + return 0; + default: + PrintUsage(argv[0]); + return -1; + } + } + return 1; +} + +static int ProvisionGatewayDeviceHandler(struct ubus_context *ctx, struct ubus_object *obj, + struct ubus_request_data *req, const char *method, struct blob_attr *msg) +{ + struct blob_attr *args[PROVISION_GATEWAY_DEVICE_MAX]; + struct blob_buf b = {0}; + + blobmsg_parse(provisionGatewayDevicePolicy, PROVISION_GATEWAY_DEVICE_MAX, args, blob_data(msg), blob_len(msg)); + if (!args[ARG_DEVICE_NAME] || !args[ARG_DEVICE_TYPE] || !args[ARG_LICENSEE_ID] || !args[ARG_FCAP] || !args[ARG_LICENSEE_SECRET]) + return UBUS_STATUS_INVALID_ARGUMENT; + + char *deviceName = blobmsg_get_string(args[ARG_DEVICE_NAME]); + char *deviceType = blobmsg_get_string(args[ARG_DEVICE_TYPE]); + char *fcap = blobmsg_get_string(args[ARG_FCAP]); + int licenseeID = blobmsg_get_u32(args[ARG_LICENSEE_ID]); + char *licenseeSecret = blobmsg_get_string(args[ARG_LICENSEE_SECRET]); + + if (!deviceName || !deviceType || !fcap || !licenseeSecret) + return UBUS_STATUS_UNKNOWN_ERROR; + + ProvisionStatus status = ProvisionGatewayDevice(deviceName, deviceType, licenseeID, fcap, licenseeSecret); + + blob_buf_init(&b, 0); + blobmsg_add_u32(&b, "provision_status", status); + ubus_send_reply(ctx, req, b.head); + blob_buf_free(&b); + + return UBUS_STATUS_OK; +} + +static int IsGatewayDeviceProvisionedHandler(struct ubus_context *ctx, struct ubus_object *obj, + struct ubus_request_data *req, const char *method, struct blob_attr *msg) +{ + struct blob_buf b = {0}; + blob_buf_init(&b, 0); + bool ret = IsGatewayDeviceProvisioned(); + blobmsg_add_u8(&b, "provision_status", ret); + ubus_send_reply(ctx, req, b.head); + blob_buf_free(&b); + return UBUS_STATUS_OK; +} + +static int GetClientListHandler(struct ubus_context *ctx, struct ubus_object *obj, + struct ubus_request_data *req, const char *method, struct blob_attr *msg) +{ + struct blob_buf b = {0}; + blob_buf_init(&b, 0); + json_object *respObj = json_object_new_object(); + GetClientList(respObj); + blobmsg_add_json_from_string(&b, json_object_get_string(respObj)); + ubus_send_reply(ctx, req, b.head); + blob_buf_free(&b); + json_object_put(respObj); + return UBUS_STATUS_OK; +} + + +static int ProvisionConstrainedDeviceHandler(struct ubus_context *ctx, struct ubus_object *obj, + struct ubus_request_data *req, const char *method, struct blob_attr *msg) +{ + struct blob_attr *args[PROVISION_CONSTRAINED_DEVICE_MAX]; + struct blob_buf b = {0}; + int timeout; + + blobmsg_parse(provisionConstrainedDevicePolicy, PROVISION_CONSTRAINED_DEVICE_MAX, args, blob_data(msg), blob_len(msg)); + if (!args[ARG_CONSTRAINED_DEVICE_TYPE] || !args[ARG_CONSTRAINED_LICENSEE_ID] || + !args[ARG_CONSTRAINED_CLIENT_ID] || !args[ARG_CONSTRAINED_FCAP] || + !args[ARG_CONSTRAINED_PARENT_ID]) + return UBUS_STATUS_INVALID_ARGUMENT; + + char *deviceType = blobmsg_get_string(args[ARG_CONSTRAINED_DEVICE_TYPE]); + int licenseeID = blobmsg_get_u32(args[ARG_CONSTRAINED_LICENSEE_ID]); + char *clientID = blobmsg_get_string(args[ARG_CONSTRAINED_CLIENT_ID]); + char *fcap = blobmsg_get_string(args[ARG_CONSTRAINED_FCAP]); + char *parentID = blobmsg_get_string(args[ARG_CONSTRAINED_PARENT_ID]); + + if (!args[ARG_CONSTRAINED_TIMEOUT]) + timeout = DEFAULT_PROVSIONING_TIMEOUT; + else + timeout = blobmsg_get_u32(args[ARG_CONSTRAINED_TIMEOUT]); + + if (!deviceType || !clientID || !fcap || !parentID) + return UBUS_STATUS_UNKNOWN_ERROR; + + ProvisionStatus status = ProvisionConstrainedDevice(clientID, fcap, deviceType, licenseeID, parentID, timeout); + + blob_buf_init(&b, 0); + blobmsg_add_u32(&b, "status", status); + ubus_send_reply(ctx, req, b.head); + blob_buf_free(&b); + + return UBUS_STATUS_OK; +} + +static int IsConstrainedDeviceProvisionedHandler(struct ubus_context *ctx, struct ubus_object *obj, + struct ubus_request_data *req, const char *method, struct blob_attr *msg) +{ + struct blob_attr *args[IS_CONSTRAINED_DEVICE_PROVISIONED_MAX]; + struct blob_buf b = {0}; + + blobmsg_parse(isConstrainedDeviceProvisionedPolicy, IS_CONSTRAINED_DEVICE_PROVISIONED_MAX, args, blob_data(msg), blob_len(msg)); + if (!args[ARG_CLIENT_ID]) + return UBUS_STATUS_INVALID_ARGUMENT; + char *clientID = blobmsg_get_string(args[ARG_CLIENT_ID]); + if (!clientID) + return UBUS_STATUS_UNKNOWN_ERROR; + + bool ret = IsConstrainedDeviceProvisioned(clientID); + + blob_buf_init(&b, 0); + blobmsg_add_u8(&b, "provision_status", ret); + ubus_send_reply(ctx, req, b.head); + blob_buf_free(&b); + return UBUS_STATUS_OK; +} +//! \} + +/** +* @brief Entry point of application. +*/ +int main(int argc, char **argv) +{ + int ret; + CmdOpts cmdOpts; + FILE *logFile = NULL; + struct ubus_context *ctx; + const char *path = NULL; + + struct ubus_method flowDeviceManagerMethods[] = + { + UBUS_METHOD("provision_gateway_device", ProvisionGatewayDeviceHandler, provisionGatewayDevicePolicy), + UBUS_METHOD("provision_constrained_device", ProvisionConstrainedDeviceHandler, provisionConstrainedDevicePolicy), + UBUS_METHOD("is_constrained_device_provisioned", IsConstrainedDeviceProvisionedHandler, isConstrainedDeviceProvisionedPolicy), + UBUS_METHOD_NOARG("is_gateway_device_provisioned", IsGatewayDeviceProvisionedHandler), + UBUS_METHOD_NOARG("get_client_list", GetClientListHandler) + }; + struct ubus_object_type flowDeviceManagerObjectType = UBUS_OBJECT_TYPE("device_manager", flowDeviceManagerMethods); + struct ubus_object ubusObject = + { + .type = &flowDeviceManagerObjectType, + .name = flowDeviceManagerObjectType.name, + .methods = flowDeviceManagerObjectType.methods, + .n_methods = flowDeviceManagerObjectType.n_methods + }; + + ret = ParseCommandArgs(argc, argv, &cmdOpts); + if (ret <= 0) + return ret; + if (cmdOpts.logFile) + logFile = SetLogFile(cmdOpts.logFile); + SetDebugLevel(cmdOpts.debugLevel); + + if (!EstablishSession()) + return -1; + + uloop_init(); + ctx = ubus_connect(path); + if (!ctx) + { + LOG(LOG_ERR, "Failed to connect to ubus"); + return -1; + } + if (ret = ubus_add_object(ctx, &ubusObject)) + { + LOG(LOG_ERR, "Couldn't add object : %s", ubus_strerror(ret)); + ubus_free(ctx); + return -1; + } + ubus_add_uloop(ctx); + uloop_run(); + + ReleaseSession(); + if (logFile) + fclose(logFile); + + ubus_remove_object(ctx, &ubusObject); + ubus_free(ctx); + uloop_done(); + return 0; +} diff --git a/src/fdm_common.h b/src/fdm_common.h new file mode 100644 index 0000000..b751cfd --- /dev/null +++ b/src/fdm_common.h @@ -0,0 +1,185 @@ +/*************************************************************************************************** + * Copyright (c) 2016, Imagination Technologies Limited and/or its affiliated group companies + * and/or licensors + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used to + * endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY + * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @file fdm_common.h + * @brief Header file for exposing structure definitions and enums. + */ + +#ifndef FDM_COMMON_H +#define FDM_COMMON_H + +#include "awa/client.h" + +//! \{ +#define URL_PATH_SIZE (16) +#define MAX_STR_SIZE (64) +#define IPC_TIMEOUT (1000) +#define SLEEP_COUNT (2) +#define OBJECT_INSTANCE_ID (0) +#define DEVICE_ID_SIZE (16) +#define ARRAY_SIZE(arr) (sizeof(arr)/sizeof(arr[0])) + +#define SERVER_ADDRESS "127.0.0.1" +#define SERVER_PORT 54321 + + +#define MAKE_RESOURCE_PATH(path, objectId, resourceId) \ + AwaAPI_MakeResourcePath(path, URL_PATH_SIZE, objectId, OBJECT_INSTANCE_ID, resourceId) + +#define MAKE_FLOW_OBJECT_RESOURCE_PATH(path, resourceId) \ + MAKE_RESOURCE_PATH(path, Lwm2mObjectId_FlowObject, resourceId) + +#define MAKE_FLOW_ACCESS_OBJECT_RESOURCE_PATH(path, resourceId) \ + MAKE_RESOURCE_PATH(path, Lwm2mObjectId_FlowAccess, resourceId) + +#define MAKE_FLOW_OBJECT_INSTANCE_PATH(path) \ + AwaAPI_MakeObjectInstancePath(path, URL_PATH_SIZE, Lwm2mObjectId_FlowObject, \ + OBJECT_INSTANCE_ID) + +#define MAKE_FLOW_ACCESS_OBJECT_PATH(path) \ + AwaAPI_MakeObjectPath(path, URL_PATH_SIZE, Lwm2mObjectId_FlowAccess) +//! \} + +/** + * lwm2m object ids enum. + */ +typedef enum { + Lwm2mObjectId_DeviceObject = 3, + Lwm2mObjectId_FlowObject = 20000, + Lwm2mObjectId_FlowAccess +} Lwm2mObjectId; + +/** + * Flow object resources enum. + */ +typedef enum { + FlowObjectResourceId_DeviceId, + FlowObjectResourceId_ParentId, + FlowObjectResourceId_DeviceType, + FlowObjectResourceId_DeviceName, + FlowObjectResourceId_Description, + FlowObjectResourceId_Fcap, + FlowObjectResourceId_LicenseeId, + FlowObjectResourceId_LicenseeChallenge, + FlowObjectResourceId_HashIterations, + FlowObjectResourceId_LicenseeHash, + FlowObjectResourceId_Status +} FlowObjectResourceId; + +/** + * Flow access resources enum. + */ +typedef enum { + FlowAccessResourceId_Url, + FlowAccessResourceId_CustomerKey, + FlowAccessResourceId_CustomerSecret, + FlowAccessResourceId_RememberMeToken, + FlowAccessResourceId_RememberMeTokenExpiry +} FlowAccessResourceId; + +/** + * Device object resources enum + */ +typedef enum { + DeviceObjectResourceId_SerialNumber = 2, + DeviceObjectResourceId_SoftwareVersion = 19 +} DeviceObjectResourceId; + +/** + * A structure to contain resource information. + */ +typedef struct +{ + //! \{ + AwaResourceID id; + const char *name; + AwaResourceType type; + bool isMandatory; + bool wantToSave; + //! \} +} RESOURCE_T; + +/** + * A structure to contain object information. + */ +typedef struct +{ + //! \{ + const char *name; + AwaObjectID id; + unsigned int numResources; + RESOURCE_T *resources; + //! \} +} OBJECT_T; + +extern const OBJECT_T flowObject, flowAccessObject; +/** + * lwm2m objects subscriptions. + */ +typedef struct +{ + //! \{ + AwaClientChangeSubscription *flowObjectChange; + AwaClientChangeSubscription *flowAccessObjectChange; + //! \} +} FlowSubscriptions; + +/** + * Verification details. + */ +typedef struct +{ + //! \{ + AwaOpaque challenge; + AwaInteger iterations; + AwaOpaque licenseeHash; + bool hasChallenge; + bool hasIterations; + bool waitForServerResponse; + bool verifyLicensee; + bool isProvisionSuccess; + //! \} +} Verification; + + + +/** + * Constrained Device information. + */ +typedef struct { + //! \{ + bool isDevicePresent; + bool isFlowObjectInstanceRegistered; + bool isFlowAccessInstanceRegistered; + //! \} +} DeviceStatus; + +#endif /* FDM_COMMON_H */ diff --git a/src/fdm_get_client_list.c b/src/fdm_get_client_list.c new file mode 100644 index 0000000..e05d5fb --- /dev/null +++ b/src/fdm_get_client_list.c @@ -0,0 +1,118 @@ +/*************************************************************************************************** + * Copyright (c) 2016, Imagination Technologies Limited and/or its affiliated group companies + * and/or licensors + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used to + * endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY + * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @file fdm_get_client_list.c + * @brief Lists clients registered on the Awa LWM2M server. + */ + +/*************************************************************************************************** + * Includes + **************************************************************************************************/ +#include +#include +#include "awa/server.h" +#include "fdm_server_session.h" +#include "fdm_log.h" +#include "fdm_common.h" +#include "fdm_provision_constrained.h" + +/*************************************************************************************************** + * Macros + **************************************************************************************************/ + +//! \{ +#define LIST_CLIENTS_OPERATION_TIMEOUT 5000 +//! \} + +/*************************************************************************************************** + * Methods + **************************************************************************************************/ + +//! \{ +static void ListClients(const AwaServerSession *session, json_object *respObj) +{ + AwaError error; + json_object *listObj = json_object_new_array(); + AwaServerListClientsOperation *operation = AwaServerListClientsOperation_New(session); + if (operation == NULL) + { + LOG(LOG_ERR, "Failed to create new ListClientsOperation"); + json_object_put(listObj); + return; + } + error = AwaServerListClientsOperation_Perform(operation, LIST_CLIENTS_OPERATION_TIMEOUT); + if (error == AwaError_Success) + { + AwaClientIterator *clientIterator = AwaServerListClientsOperation_NewClientIterator(operation); + if (clientIterator != NULL) + { + while(AwaClientIterator_Next(clientIterator)) + { + const char *clientID = AwaClientIterator_GetClientID(clientIterator); + const AwaServerListClientsResponse *response = AwaServerListClientsOperation_GetResponse(operation, clientID); + json_bool provisionStatus = IsFlowAccessInstanceRegistered(session, response) ? 1 : 0; + json_object *clientObj = json_object_new_object(); + json_object_object_add(clientObj, "clientId", json_object_new_string(clientID)); + json_object_object_add(clientObj, "is_device_provisioned", json_object_new_boolean(provisionStatus)); + json_object_array_add(listObj, clientObj); + + } + AwaClientIterator_Free(&clientIterator); + } + else + { + LOG(LOG_ERR, "Failed to create new list clients iterator"); + } + } + else + { + LOG(LOG_ERR, "Failed to perform list clients operation\nerror: %s", AwaError_ToString(error)); + } + + if (AwaServerListClientsOperation_Free(&operation) != AwaError_Success) + { + LOG(LOG_ERR, "Failed to free list clients operation"); + } + + json_object_object_add(respObj, "clients", listObj); +} +//! \} + +void GetClientList(json_object *respObj) +{ + AwaServerSession *session = NULL; + session = Server_EstablishSession(SERVER_ADDRESS, SERVER_PORT); + if (session != NULL) + { + ListClients(session, respObj); + Server_ReleaseSession(&session); + } +} diff --git a/src/fdm_hmac.c b/src/fdm_hmac.c new file mode 100644 index 0000000..91dc548 --- /dev/null +++ b/src/fdm_hmac.c @@ -0,0 +1,291 @@ +/*************************************************************************************************** + * Copyright (c) 2016, Imagination Technologies Limited and/or its affiliated group companies + * and/or licensors + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used to + * endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY + * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @file fdm_hmac.c + * @brief Provides operation for computing hash. + */ + +/*************************************************************************************************** + * Includes + **************************************************************************************************/ + +#include +#include +#include + +/*************************************************************************************************** + * Macros + **************************************************************************************************/ + +//! @cond Doxygen Suppress +#define SHA256_HASH_LENGTH 32 +#define MAX_KEY_LENGTH 64 +#define MAX_BUFFER_LENGTH 1024 + +// DBL_INT_ADD treats two unsigned ints a and b as one 64-bit integer and adds c to it +#define DBL_INT_ADD(a,b,c) if (a > 0xffffffff - (c)) ++b; a += c; +#define ROTLEFT(a,b) (((a) << (b)) | ((a) >> (32-(b)))) +#define ROTRIGHT(a,b) (((a) >> (b)) | ((a) << (32-(b)))) + +#define CH(x,y,z) (((x) & (y)) ^ (~(x) & (z))) +#define MAJ(x,y,z) (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z))) +#define EP0(x) (ROTRIGHT(x,2) ^ ROTRIGHT(x,13) ^ ROTRIGHT(x,22)) +#define EP1(x) (ROTRIGHT(x,6) ^ ROTRIGHT(x,11) ^ ROTRIGHT(x,25)) +#define SIG0(x) (ROTRIGHT(x,7) ^ ROTRIGHT(x,18) ^ ((x) >> 3)) +#define SIG1(x) (ROTRIGHT(x,17) ^ ROTRIGHT(x,19) ^ ((x) >> 10)) + +#define PUT_UINT32(n,b,i) \ +{ \ + (b)[(i) ] = (uint8_t) ( (n) >> 24 ); \ + (b)[(i) + 1] = (uint8_t) ( (n) >> 16 ); \ + (b)[(i) + 2] = (uint8_t) ( (n) >> 8 ); \ + (b)[(i) + 3] = (uint8_t) ( (n) ); \ +} + + +/*************************************************************************************************** + * Typedefs + **************************************************************************************************/ + +typedef struct +{ + uint8_t Data[64]; + uint32_t DataLen; + uint32_t BitLen[2]; + uint32_t State[8]; +} Sha256ContextType; + +/*************************************************************************************************** + * Globals + **************************************************************************************************/ + +static const uint32_t k[64] = +{ + 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, + 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, + 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, + 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, + 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, + 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, + 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, + 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 +}; + +/*************************************************************************************************** + * Methods + **************************************************************************************************/ + +static void Sha256_Transform(Sha256ContextType *context, uint8_t * data) +{ + uint32_t a,b,c,d,e,f,g,h,i,j,t1,t2,m[64]; + + for (i = 0, j = 0; i < 16; ++i, j += 4) + { + m[i] = (data[j] << 24) | (data[j+1] << 16) | (data[j+2] << 8) | (data[j+3]); + } + + for (; i < 64; i++) + { + m[i] = SIG1(m[i-2]) + m[i-7] + SIG0(m[i-15]) + m[i-16]; + } + + a = context->State[0]; + b = context->State[1]; + c = context->State[2]; + d = context->State[3]; + e = context->State[4]; + f = context->State[5]; + g = context->State[6]; + h = context->State[7]; + + for (i = 0; i < 64; i++) + { + t1 = h + EP1(e) + CH(e,f,g) + k[i] + m[i]; + t2 = EP0(a) + MAJ(a,b,c); + h = g; + g = f; + f = e; + e = d + t1; + d = c; + c = b; + b = a; + a = t1 + t2; + } + + context->State[0] += a; + context->State[1] += b; + context->State[2] += c; + context->State[3] += d; + context->State[4] += e; + context->State[5] += f; + context->State[6] += g; + context->State[7] += h; +} + +static void Sha256_Init(Sha256ContextType *context) +{ + context->DataLen = 0; + context->BitLen[0] = 0; + context->BitLen[1] = 0; + context->State[0] = 0x6a09e667; + context->State[1] = 0xbb67ae85; + context->State[2] = 0x3c6ef372; + context->State[3] = 0xa54ff53a; + context->State[4] = 0x510e527f; + context->State[5] = 0x9b05688c; + context->State[6] = 0x1f83d9ab; + context->State[7] = 0x5be0cd19; +} + +static void Sha256_Update(Sha256ContextType *context, const uint8_t *data, uint32_t dataLen) +{ + uint32_t i; + + for (i = 0; i < dataLen; i++) + { + context->Data[context->DataLen] = data[i]; + context->DataLen++; + if (context->DataLen == 64) + { + Sha256_Transform(context,context->Data); + DBL_INT_ADD(context->BitLen[0], context->BitLen[1], 512); + context->DataLen = 0; + } + } +} + +static void Sha256_Final(Sha256ContextType *context, uint8_t hash[]) +{ + uint32_t i; + + i = context->DataLen; + + // Pad whatever data is left in the buffer. + if (context->DataLen < 56) + { + context->Data[i++] = 0x80; + while (i < 56) + context->Data[i++] = 0x00; + } + else + { + context->Data[i++] = 0x80; + while (i < 64) + context->Data[i++] = 0x00; + + Sha256_Transform(context,context->Data); + memset(context->Data, 0, 56); + } + + // Append to the padding the total message's length in bits and transform. + DBL_INT_ADD(context->BitLen[0], context->BitLen[1], context->DataLen * 8); + PUT_UINT32(context->BitLen[0], context->Data, 60); + PUT_UINT32(context->BitLen[1], context->Data, 56); + Sha256_Transform(context, context->Data); + + // Copy result into output buffer + PUT_UINT32(context->State[0], hash, 0); + PUT_UINT32(context->State[1], hash, 4); + PUT_UINT32(context->State[2], hash, 8); + PUT_UINT32(context->State[3], hash, 12); + PUT_UINT32(context->State[4], hash, 16); + PUT_UINT32(context->State[5], hash, 20); + PUT_UINT32(context->State[6], hash, 24); + PUT_UINT32(context->State[7], hash, 28); +} +//! @endcond + +/** + * @brief Compute a Hash of the data provided using SHA256 and write the result into a buffer + * @param[out] hash - buffer to store resulting hash + * @param[in] data - pointer to data to hash + * @param[in] dataLen - length of data in buffer + */ +static void Sha256_ComputeHash(uint8_t hash[SHA256_HASH_LENGTH], const uint8_t *data, int dataLen) +{ + Sha256ContextType context; + Sha256_Init(&context); + Sha256_Update(&context,data, dataLen); + Sha256_Final(&context, hash); +} + +/** + * @brief Compute a Hmac using SHA256 of the data provided and write the result into a buffer + * @param[out] hash - buffer to store resulting hash + * @param[in] data - pointer to data to hash + * @param[in] dataLen - length of data in buffer + * @param[in] key - pointer to key + * @param[in] keyLen - length of key + */ +void HmacSha256_ComputeHash(uint8_t hash[SHA256_HASH_LENGTH], const uint8_t *data, int dataLen, + const uint8_t *key, int keyLen) +{ + uint8_t innerKeyPad[MAX_KEY_LENGTH]; + uint8_t outerKeyPad[MAX_KEY_LENGTH]; + uint8_t keyHash[SHA256_HASH_LENGTH]; + uint8_t innerHash[SHA256_HASH_LENGTH]; + uint8_t buffer[MAX_BUFFER_LENGTH]; + unsigned int i; + + // if key is longer than 64 bytes use the hash of the key as the key. + if (keyLen > MAX_KEY_LENGTH) + { + Sha256_ComputeHash(keyHash, key, keyLen); + key = keyHash; + keyLen = SHA256_HASH_LENGTH; + } + + // copy key to pad values, we will then XOR this with 0x36 and 0x5c respectively + memset(innerKeyPad, 0, sizeof(innerKeyPad)); + memset(outerKeyPad, 0, sizeof(outerKeyPad)); + memcpy(innerKeyPad, key, keyLen); + memcpy(outerKeyPad, key, keyLen); + + for (i = 0; i < MAX_KEY_LENGTH; i++) + { + innerKeyPad[i] ^= 0x36; + outerKeyPad[i] ^= 0x5c; + } + + // perform inner SHA256 + memset(buffer, 0x00, sizeof(buffer)); + memcpy(buffer, innerKeyPad, MAX_KEY_LENGTH); + memcpy(buffer + MAX_KEY_LENGTH, data, dataLen); + + Sha256_ComputeHash(innerHash, buffer, MAX_KEY_LENGTH + dataLen); + + // perform outer SHA256 + memset(buffer, 0x00, sizeof(buffer)); + memcpy(buffer, outerKeyPad, MAX_KEY_LENGTH); + memcpy(buffer + MAX_KEY_LENGTH, innerHash, SHA256_HASH_LENGTH); + + Sha256_ComputeHash(hash, buffer, MAX_KEY_LENGTH + SHA256_HASH_LENGTH); +} diff --git a/src/fdm_hmac.h b/src/fdm_hmac.h new file mode 100644 index 0000000..b3fe23e --- /dev/null +++ b/src/fdm_hmac.h @@ -0,0 +1,67 @@ +/*************************************************************************************************** + * Copyright (c) 2016, Imagination Technologies Limited and/or its affiliated group companies + * and/or licensors + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used to + * endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY + * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @file fdm_hmac.h + * @brief Provides provisioning operations. + */ + +/*************************************************************************************************** + * Includes + **************************************************************************************************/ + +#ifndef HMAC_256_H +#define HMAC_256_H + +//! \{ +#define SHA256_HASH_LENGTH 32 +//! \} + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Compute a Hmac using SHA256 of the data provided and write the result into a buffer + * @param[out] hash - buffer to store resulting hash + * @param[in] data - pointer to data to hash + * @param[in] dataLen - length of data in buffer + * @param[in] key - pointer to key + * @param[in] keyLen - length of key + */ +void HmacSha256_ComputeHash(uint8_t hash[SHA256_HASH_LENGTH], const uint8_t * data, int dataLen, const uint8_t * key, int keyLen); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/fdm_licensee_verification.c b/src/fdm_licensee_verification.c new file mode 100644 index 0000000..1ceb3fa --- /dev/null +++ b/src/fdm_licensee_verification.c @@ -0,0 +1,145 @@ +/*************************************************************************************************** + * Copyright (c) 2016, Imagination Technologies Limited and/or its affiliated group companies + * and/or licensors + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used to + * endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY + * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @file fdm_licensee_verification.c + * @brief Provides licensee verification operations. + */ + +/*************************************************************************************************** + * Includes + **************************************************************************************************/ + +#include +#include +#include +#include "awa/client.h" +#include "fdm_hmac.h" +#include "fdm_register.h" +#include "fdm_common.h" +#include "fdm_log.h" + +/*************************************************************************************************** + * Methods + **************************************************************************************************/ + +/** +* @brief Calculate the Licensee Hash based-on challenge. +* @param[out] hash Hash of licensee data calculated from challenge and hash iterations. +* @param[in] challenge Licensee challenge. +* @param[in] challengeLength length of challenge. +* @param[in] iterations Hash iterations. +* @param[in] licenseeSecret Licensee Secret. +* @return true for success otherwise false. +*/ +static bool CalculateLicenseeHash(uint8_t hash[SHA256_HASH_LENGTH], const char *challenge, + int challengeLength, int iterations, const char *licenseeSecret) +{ + unsigned int i; + uint8_t key[MAX_STR_SIZE]; + int keyLength; + + if (challenge == NULL || licenseeSecret == NULL) + { + LOG(LOG_ERR, "Null params passed to %s()", __func__); + return false; + } + + LOG(LOG_DBG, "Calculating licensee hash"); + + keyLength = b64Decode((char *)key, sizeof(key), licenseeSecret, strlen(licenseeSecret)); + + if (keyLength == -1) + { + LOG(LOG_ERR, "Failed to decode a base64 encoded value"); + return false; + } + + HmacSha256_ComputeHash(hash, (const uint8_t *) challenge, challengeLength, key, keyLength); + for (i = 1; i < iterations; i++) + { + HmacSha256_ComputeHash(hash, hash, SHA256_HASH_LENGTH, key, keyLength); + } + return true; +} + +bool PerformFlowLicenseeVerification(AwaClientSession *session, Verification *verificationData, const char *licenseeSecret) +{ + uint8_t licenseeHash[SHA256_HASH_LENGTH] = {0}; + char licenseeHashResourcePath[URL_PATH_SIZE] = {0}; + AwaError error; + + if (session == NULL || verificationData == NULL) + { + LOG(LOG_ERR, "Null params passed to %s()", __func__); + return false; + } + + LOG(LOG_INFO, "Performing flow license verification"); + + // Calculate the LicenseeHash based-on challenge + if (!CalculateLicenseeHash(licenseeHash, verificationData->challenge.Data, + verificationData->challenge.Size, verificationData->iterations, licenseeSecret)) + { + LOG(LOG_ERR, "Failed to calculate licensee hash"); + verificationData->waitForServerResponse = false; + return false; + } + + if (verificationData->licenseeHash.Data != NULL) + { + free(verificationData->licenseeHash.Data); + } + + verificationData->licenseeHash.Data = malloc(SHA256_HASH_LENGTH); + + if (verificationData->licenseeHash.Data != NULL) + { + memcpy(verificationData->licenseeHash.Data , licenseeHash, SHA256_HASH_LENGTH); + } + + verificationData->licenseeHash.Size = SHA256_HASH_LENGTH; + + // Write the hash to the Flow object + if ((error = MAKE_FLOW_OBJECT_RESOURCE_PATH(licenseeHashResourcePath, FlowObjectResourceId_LicenseeHash)) == AwaError_Success) + { + if(!SetResource(session, licenseeHashResourcePath, (void *)&verificationData->licenseeHash, AwaResourceType_Opaque)) + { + LOG(LOG_ERR, "Failed to set licensee hash"); + return false; + } + } + else + { + LOG(LOG_ERR, "Failed to create licensee hash resource path\nerror: %s", AwaError_ToString(error)); + return false; + } + return true; +} diff --git a/src/fdm_licensee_verification.h b/src/fdm_licensee_verification.h new file mode 100644 index 0000000..2555355 --- /dev/null +++ b/src/fdm_licensee_verification.h @@ -0,0 +1,48 @@ +/*************************************************************************************************** + * Copyright (c) 2016, Imagination Technologies Limited and/or its affiliated group companies + * and/or licensors + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used to + * endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY + * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @file fdm_licensee_verification.h + * @brief Header file for exposing licensee verification operation. + */ + +#ifndef FDM_LICENSEE_VERIFICATION_H +#define FDM_LICENSEE_VERIFICATION_H + +/** + * @brief Perform flow licensee verification using challenge and hash iterations and set the licensee hash to the flow object. + * @param[in] session A pointer to a valid session. + * @param[in] verificationData Licensee verification data. + * @param[in] licenseeSecret Licensee Secret. + * @return true for success otherwise false. + */ +bool PerformFlowLicenseeVerification(AwaClientSession *session, Verification *verificationData, const char *licenseeSecret); + +#endif /* FDM_LICENSEE_VERIFICATION_H */ diff --git a/src/fdm_log.h b/src/fdm_log.h new file mode 100644 index 0000000..59ecaa7 --- /dev/null +++ b/src/fdm_log.h @@ -0,0 +1,107 @@ +/*************************************************************************************************** + * Copyright (c) 2016, Imagination Technologies Limited and/or its affiliated group companies + * and/or licensors + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used to + * endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY + * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @file fdm_log.h + * @brief Header file for logging. + */ + +#ifndef FDM_LOG_H +#define FDM_LOG_H + +#include +#include +#include + +//! \{ +#define ANSI_COLOR_RED "\x1b[31m" +#define ANSI_COLOR_YELLOW "\x1b[33m" +#define ANSI_COLOR_CYAN "\x1b[36m" +#define ANSI_COLOR_RESET "\x1b[0m" + +#define LOG_FATAL (1) +#define LOG_ERR (2) +#define LOG_WARN (3) +#define LOG_INFO (4) +#define LOG_DBG (5) +#define __FILENAME__ (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__) + +#define TIME_BUFFER_SIZE (32) +//! \} + +/** Macro for printing logging message with current time, function and line no. */ +#define DEBUG_PRINT \ + do { \ + time_t currentTime = time(NULL); \ + char buffer[TIME_BUFFER_SIZE] = {0}; \ + strftime(buffer, TIME_BUFFER_SIZE, "%x %X", localtime(¤tTime)); \ + fprintf(debugStream,"[%s] ", buffer); \ + fprintf(debugStream, ANSI_COLOR_YELLOW); \ + fprintf(debugStream,"%s:%d: ", __FILENAME__, __LINE__); \ + fprintf(debugStream, ANSI_COLOR_RESET); \ + } while (0) + +/** Macro for logging message at the specified level. */ +#define LOG(level, ...) \ + do { \ + if (level <= debugLevel) \ + { \ + if (debugStream == NULL) \ + debugStream = stdout; \ + fprintf(debugStream, "\n"); \ + if (debugLevel == LOG_DBG) \ + { \ + DEBUG_PRINT; \ + } \ + switch (level) \ + { \ + case LOG_ERR: \ + fprintf(debugStream, ANSI_COLOR_RED); \ + break; \ + case LOG_INFO: \ + fprintf(debugStream, ANSI_COLOR_CYAN); \ + break; \ + default: \ + break; \ + } \ + fprintf(debugStream, __VA_ARGS__); \ + fprintf(debugStream, ANSI_COLOR_RESET); \ + fprintf(debugStream, "\n"); \ + fflush(debugStream); \ + } \ + } while (0) + +/** Output stream to dump logs. */ +extern FILE *debugStream; +/** Debug level for logs. */ +extern int debugLevel; + + +#endif /* FDM_LOG_H */ diff --git a/src/fdm_provision_constrained.c b/src/fdm_provision_constrained.c new file mode 100644 index 0000000..23ac2d5 --- /dev/null +++ b/src/fdm_provision_constrained.c @@ -0,0 +1,442 @@ +/*************************************************************************************************** + * Copyright (c) 2016, Imagination Technologies Limited and/or its affiliated group companies + * and/or licensors + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used to + * endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY + * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + + /** + * @file fdm_provision_constrained.c + * @brief Implementation for provisioning constrained devices + */ + +/*************************************************************************************************** + * Includes + **************************************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include "awa/common.h" +#include "awa/server.h" +#include "device_manager.h" +#include "fdm_common.h" +#include "fdm_log.h" +#include "fdm_server_session.h" +#include "fdm_register.h" + +/*************************************************************************************************** + * Definitions + **************************************************************************************************/ + +//! @cond Doxygen_Suppress +#define QUERY_TIMEOUT 5000 + +#define COAP_TIMEOUT 10000 + +#define POLLING_SLEEP_SECONDS 2 +//! @endcond + +/*************************************************************************************************** + * Typedef + **************************************************************************************************/ + +/** + * Struct to hold paths for flow objects and resources + */ +typedef struct +{ + //! \{ + char flowObjectInstancePath[URL_PATH_SIZE]; + // FlowObject resources + char fcapPath[URL_PATH_SIZE]; + char deviceTypePath[URL_PATH_SIZE]; + char licenseeIDPath[URL_PATH_SIZE]; + char parentIDPath[URL_PATH_SIZE]; + //! \} + +} Paths; + +/*************************************************************************************************** + * Globals + **************************************************************************************************/ + +//! @cond Doxygen_Suppress +static Paths pathStore; +static bool pathsMade = false; +//! @endcond + +/*************************************************************************************************** + * Implementation + **************************************************************************************************/ + +/** + * @brief Generate Object and Resource paths. + * @return true if object and resource paths are generated successfully, else false. + */ +static bool MakePaths(void) +{ + memset(&pathStore, 0, sizeof(Paths)); + if (AwaAPI_MakeObjectInstancePath(pathStore.flowObjectInstancePath, URL_PATH_SIZE, + Lwm2mObjectId_FlowObject, 0) != AwaError_Success || + // FlowObject resources + AwaAPI_MakeResourcePath(pathStore.fcapPath, URL_PATH_SIZE, Lwm2mObjectId_FlowObject, 0, + FlowObjectResourceId_Fcap) != AwaError_Success || + AwaAPI_MakeResourcePath(pathStore.deviceTypePath, URL_PATH_SIZE, + Lwm2mObjectId_FlowObject, 0, + FlowObjectResourceId_DeviceType) != AwaError_Success || + AwaAPI_MakeResourcePath(pathStore.licenseeIDPath, URL_PATH_SIZE, + Lwm2mObjectId_FlowObject, 0, + FlowObjectResourceId_LicenseeId) != AwaError_Success || + AwaAPI_MakeResourcePath(pathStore.parentIDPath, URL_PATH_SIZE, + Lwm2mObjectId_FlowObject, 0, + FlowObjectResourceId_ParentId) != AwaError_Success) + { + LOG(LOG_ERR, "Couldn't generate all object and resource paths"); + return false; + } + + return true; +} + +/** + * @brief Check if flow access instance is registered or not. + * @param[in] session Holds server session. + * @param[in] clientListResponse List of responses from client. + * @return true if flow access instance found, else false. + */ +bool IsFlowAccessInstanceRegistered(const AwaServerSession *session, const AwaServerListClientsResponse *clientListResponse) +{ + bool found = false; + AwaRegisteredEntityIterator *objectIterator = AwaServerListClientsResponse_NewRegisteredEntityIterator(clientListResponse); + while (AwaRegisteredEntityIterator_Next(objectIterator)) + { + const char *path = AwaRegisteredEntityIterator_GetPath(objectIterator); + AwaObjectID objectID = AWA_INVALID_ID; + AwaObjectInstanceID objectInstanceID = AWA_INVALID_ID; + AwaServerSession_PathToIDs(session, path, &objectID, &objectInstanceID, NULL); + if (objectID == Lwm2mObjectId_FlowAccess && objectInstanceID == 0) + { + LOG(LOG_DBG, "Flow Access Instance Found"); + found = true; + break; + } + } + AwaRegisteredEntityIterator_Free(&objectIterator); + return found; +} + +/** + * @brief Check if flow object instance is registered or not. + * @param[in] session Holds server session. + * @param[in] clientListResponse List of responses from client. + * @return true if flow object instance found, else false. + */ +bool IsFlowObjectInstanceRegistered(const AwaServerSession *session, const AwaServerListClientsResponse *clientListResponse) +{ + bool found = false; + AwaRegisteredEntityIterator *objectIterator = AwaServerListClientsResponse_NewRegisteredEntityIterator(clientListResponse); + while (AwaRegisteredEntityIterator_Next(objectIterator)) + { + const char *path = AwaRegisteredEntityIterator_GetPath(objectIterator); + AwaObjectID objectID = AWA_INVALID_ID; + AwaObjectInstanceID objectInstanceID = AWA_INVALID_ID; + AwaServerSession_PathToIDs(session, path, &objectID, &objectInstanceID, NULL); + if (objectID == Lwm2mObjectId_FlowObject && objectInstanceID == 0) + { + LOG(LOG_DBG, "Flow Object Instance Found"); + found = true; + break; + } + } + AwaRegisteredEntityIterator_Free(&objectIterator); + return found; +} + +/** + * @brief Get device's flow object and flow access instance status. + * @param[in] session Holds server session. + * @param[in] clientID Holds ID of registered client. + * @param[in] deviceStatus Pointer to structure holding device status information. + * @return true if status is retrieved successfully, else false. + */ +static bool GetDeviceStatus(const AwaServerSession *session, const char *clientID, DeviceStatus *deviceStatus) +{ + AwaError error = AwaError_Unspecified; + bool result = true; + + if (session == NULL || clientID == NULL || deviceStatus == NULL) + { + LOG(LOG_ERR, "Null arguments to %s()", __func__); + return false; + } + + AwaServerListClientsOperation *clientListOperation = AwaServerListClientsOperation_New(session); + if (clientListOperation == NULL) + { + LOG(LOG_ERR, "Failed to create new client list operation"); + return false; + } + + error = AwaServerListClientsOperation_Perform(clientListOperation, QUERY_TIMEOUT); + if (error == AwaError_Success) + { + const AwaServerListClientsResponse *response = AwaServerListClientsOperation_GetResponse(clientListOperation, clientID); + deviceStatus->isDevicePresent = (response != NULL); + if (deviceStatus->isDevicePresent) + { + deviceStatus->isFlowAccessInstanceRegistered = IsFlowAccessInstanceRegistered(session, response); + deviceStatus->isFlowObjectInstanceRegistered = IsFlowObjectInstanceRegistered(session, response); + } + else + { + deviceStatus->isFlowObjectInstanceRegistered = false; + deviceStatus->isFlowAccessInstanceRegistered = false; + } + } + else + { + LOG(LOG_ERR, "Failed to perform list clients operation"); + } + AwaServerListClientsOperation_Free(&clientListOperation); + return true; +} + +/** + * @brief Write to the parent ID resource of flow object of constrained device. + * @param[in] session Holds server session. + * @param[in] clientID Holds ID of registered client. + * @param[in] parentID Parent ID to be assigned. + * @return true if parent ID written successfully, else false. + */ +static bool WriteParentID (const AwaServerSession *session, const char *clientID, const char *parentID) +{ + unsigned char i, gatewayDeviceID[DEVICE_ID_SIZE]; + AwaOpaque parentIDOpaque; + AwaError error = AwaError_Success; + bool result = false; + // 3 because two is for hex letters and one for space + if (strlen(parentID)/3 != DEVICE_ID_SIZE) + { + LOG(LOG_ERR, "ParentID is not of %u bytes", DEVICE_ID_SIZE); + return false; + } + + AwaServerWriteOperation *writeOp = AwaServerWriteOperation_New(session, AwaWriteMode_Update); + if (writeOp != NULL) + { + for (i = 0; i < DEVICE_ID_SIZE; i++) + { + // 3 because two is for hex letters and one for space + sscanf(&parentID[i*3], "%02hhX", &gatewayDeviceID[i]); + } + parentIDOpaque.Data = gatewayDeviceID; + parentIDOpaque.Size = sizeof(gatewayDeviceID); + error = AwaServerWriteOperation_AddValueAsOpaque(writeOp, pathStore.parentIDPath, parentIDOpaque); + if (error == AwaError_Success) + { + error = AwaServerWriteOperation_Perform(writeOp, clientID, COAP_TIMEOUT); + if (error == AwaError_Success) + { + result = true; + } + else + { + LOG(LOG_ERR, "Failed to write parentID\nerror: %s", AwaError_ToString(error)); + } + } + AwaServerWriteOperation_Free(&writeOp); + } + return result; +} + +/** + * @brief Write provisioning information e.g. device type, licensee ID and fcap to device. + * @param[in] session Holds server session. + * @param[in] clientID Holds ID of registered client. + * @param[in] fcapCode Pointer to fcap code. + * @param[in] deviceType Pointer to device type. + * @param[in] licenseeID Licensee ID. + * @param[in] isFlowObjectInstanceRegistered States if flow object instance is registered or not. + * @return true if provisioning information is written successfully to device, else false. + */ +static bool WriteProvisioningInformationToDevice (const AwaServerSession *session, + const char *clientID, const char *fcapCode, const char *deviceType, int licenseeID, bool isFlowObjectInstanceRegistered) +{ + bool result = false; + AwaError error = AwaError_Success; + AwaServerWriteOperation *writeOp = AwaServerWriteOperation_New(session, AwaWriteMode_Update); + if (writeOp != NULL) + { + if (!isFlowObjectInstanceRegistered) + { + AwaServerWriteOperation_CreateObjectInstance(writeOp, pathStore.flowObjectInstancePath); + } + + error = AwaServerWriteOperation_AddValueAsCString(writeOp, pathStore.fcapPath, fcapCode); + if (error == AwaError_Success) + { + error = AwaServerWriteOperation_AddValueAsCString(writeOp, pathStore.deviceTypePath, deviceType); + } + if (error == AwaError_Success) + { + error = AwaServerWriteOperation_AddValueAsInteger(writeOp, pathStore.licenseeIDPath, licenseeID); + } + if (error == AwaError_Success) + { + error = AwaServerWriteOperation_Perform(writeOp, clientID, COAP_TIMEOUT); + if (error == AwaError_Success) + { + result = true; + } + else + { + LOG(LOG_ERR, "Failed to perform write operation\nerror: %s", AwaError_ToString(error)); + } + } + else + { + LOG(LOG_ERR, "Failed to create write request\nerror: %s", AwaError_ToString(error)); + } + AwaServerWriteOperation_Free(&writeOp); + } + return result; +} + +/** + * @brief Wait for specified timeout until provisioning is done. + * @param[in] serverSession Holds server session. + * @param[in] clientID Holds ID of registered client. + * @param[in] timeout Time to wait for provisioning to complete. + * @return true if provisioning is successful, else false. + */ +static bool WaitForProvisioning(AwaServerSession *serverSession, const char *clientID, int timeout) +{ + DeviceStatus deviceStatus; + while (timeout-- != 0) + { + GetDeviceStatus(serverSession, clientID, &deviceStatus); + if (deviceStatus.isFlowAccessInstanceRegistered) + { + return true; + } + sleep(POLLING_SLEEP_SECONDS); + } + LOG(LOG_ERR, "Failed to provision device"); + return false; +} + +bool IsConstrainedDeviceProvisioned(const char *clientID) +{ + DeviceStatus deviceStatus; + if (clientID == NULL) + { + LOG(LOG_ERR, "Null arguments to %s()", __func__); + return false; + } + + AwaServerSession *serverSession = Server_EstablishSession(SERVER_ADDRESS, SERVER_PORT); + + if (serverSession == NULL) + { + LOG(LOG_ERR, "Failed to establish session with server"); + return false; + } + GetDeviceStatus(serverSession, clientID, &deviceStatus); + Server_ReleaseSession(&serverSession); + return deviceStatus.isFlowAccessInstanceRegistered; +} + +ProvisionStatus ProvisionConstrainedDevice(const char *clientID, const char*fcap, + const char *deviceType, int licenseeID, const char *parentID, int timeout) +{ + ProvisionStatus result = PROVISION_FAIL; + OBJECT_T flowObjects[] = {flowObject, flowAccessObject}; + DeviceStatus deviceStatus; + LOG(LOG_INFO, "Provision constrained device:\n" + "\n%-11s\t = %s\n%-11s\t = %s\n%-11s\t = %d\n%-11s\t = %s", "Client ID", clientID, "Device Type", + deviceType, "Licensee ID", licenseeID, "Parent ID", parentID); + if (clientID == NULL || fcap == NULL || parentID == NULL) + { + LOG(LOG_ERR, "Null arguments to %s()", __func__); + return PROVISION_FAIL; + } + + AwaServerSession *serverSession = Server_EstablishSession(SERVER_ADDRESS, SERVER_PORT); + if (serverSession == NULL) + { + LOG(LOG_ERR, "Failed to establish session with server"); + return PROVISION_FAIL; + } + + if (!pathsMade) + { + if (!MakePaths()) + { + LOG(LOG_ERR, "Failed to create paths"); + return false; + } + pathsMade = true; + } + + if (DefineObjectsAtServer(serverSession, flowObjects, ARRAY_SIZE(flowObjects))) + { + GetDeviceStatus(serverSession, clientID, &deviceStatus); + if (!deviceStatus.isDevicePresent) + { + LOG(LOG_ERR, "Device not present"); + } + else if (deviceStatus.isFlowAccessInstanceRegistered) + { + LOG(LOG_INFO, "Device already provisioned"); + result = ALREADY_PROVISIONED; + } + else + { + if (!WriteProvisioningInformationToDevice(serverSession, clientID, fcap, deviceType, licenseeID, deviceStatus.isFlowObjectInstanceRegistered) || + !WriteParentID(serverSession, clientID, parentID)) + { + LOG(LOG_ERR, "Writing of device provisioning information failed"); + } + else if (WaitForProvisioning(serverSession, clientID, timeout)) + { + result = PROVISION_OK; + } + } + } + else + { + LOG(LOG_ERR, "Failed to register flow objects' definitions at the server"); + } + Server_ReleaseSession(&serverSession); + LOG(LOG_INFO, "status = %d", result); + return result; +} diff --git a/src/fdm_provision_constrained.h b/src/fdm_provision_constrained.h new file mode 100644 index 0000000..52fc4b7 --- /dev/null +++ b/src/fdm_provision_constrained.h @@ -0,0 +1,70 @@ +/*************************************************************************************************** + * Copyright (c) 2016, Imagination Technologies Limited and/or its affiliated group companies + * and/or licensors + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used to + * endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY + * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @file fdm_provision_constrained.h + * @brief Provides provisioning operations. + */ + +/*************************************************************************************************** + * Includes + **************************************************************************************************/ + +#ifndef FDM_PROVISION_CONSTRAINED_H +#define FDM_PROVISION_CONSTRAINED_H + +#include "device_manager.h" +#include "awa/server.h" + +/** + * @brief Provision a Constrained Device that has connected to the Gateway with FlowCloud + * @param[in] clientID User assigned name of device + * @param[in] fcap FCAP code + * @param[in] deviceType registered device type + * @param[in] licenseeID Licensee ID. + * @param[in] parentID Device ID of Gateway device. + * @param[in] timeout time to wait for notification to arrive. + * @return 0 for PROVISION_OK + 1 for PROVISION_FAIL + 2 for ALREADY_PROVISIONED + */ +ProvisionStatus ProvisionConstrainedDevice(const char *clientID, const char *fcap, + const char *deviceType, int licenseeID, const char *parentID, int timeout); + +/** + * @brief Poll the contents of the FlowAccessObject of the constrained device. + * and check if the device has been provisioned. + * @param[in] session handle to the session with server + * @param[in] clientID Name of the device + * @return true if device is provisioned else false + */ +bool IsDeviceProvisioned(const AwaServerSession *session, const char *clientID); + +#endif /* FDM_PROVISION_CONSTRAINED_H */ diff --git a/src/fdm_register.c b/src/fdm_register.c new file mode 100644 index 0000000..5754b24 --- /dev/null +++ b/src/fdm_register.c @@ -0,0 +1,677 @@ +/*************************************************************************************************** + * Copyright (c) 2016, Imagination Technologies Limited and/or its affiliated group companies + * and/or licensors + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used to + * endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY + * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @file fdm_register.c + * @brief Provides operations to register flow and flow access objects, get and set their resources + * and check their existence. + */ + +/*************************************************************************************************** + * Includes + **************************************************************************************************/ + +#include +#include +#include +#include +#include "awa/client.h" +#include "awa/server.h" +#include "awa/common.h" +#include "fdm_common.h" +#include "fdm_log.h" + +/*************************************************************************************************** + * Macros + **************************************************************************************************/ + +//! \{ +#define MIN_INSTANCES (0) +#define MAX_INSTANCES (1) +#define OPAQUE_VALUE_SIZE (32) +//! \} + +/*************************************************************************************************** + * Methods + **************************************************************************************************/ + +/** + * @brief Create AwaObjectDefinition from OBJECT_T struct. + * @param[in] object + * @return Definition for the object. + */ +static AwaObjectDefinition *CreateObjectDefinition(const OBJECT_T *object) +{ + unsigned int i; + bool result = true; + RESOURCE_T *resource; + AwaError error; + AwaObjectDefinition *awaObject = AwaObjectDefinition_New(object->id, object->name, MIN_INSTANCES, MAX_INSTANCES); + + if (awaObject == NULL) + { + LOG(LOG_ERR, "Failed to create %s definition", object->name); + return NULL; + } + + // define resources + for (i = 0; i < object->numResources; i++) + { + resource = &object->resources[i]; + + switch (resource->type) + { + case AwaResourceType_String: + error = AwaObjectDefinition_AddResourceDefinitionAsString(awaObject, + resource->id, resource->name, resource->isMandatory, + AwaResourceOperations_ReadWrite, ""); + break; + + case AwaResourceType_Integer: + error = AwaObjectDefinition_AddResourceDefinitionAsInteger(awaObject, + resource->id, resource->name, resource->isMandatory, + AwaResourceOperations_ReadWrite, 0); + break; + + case AwaResourceType_Time: + error = AwaObjectDefinition_AddResourceDefinitionAsTime(awaObject, + resource->id, resource->name, resource->isMandatory, + AwaResourceOperations_ReadWrite, 0); + break; + + case AwaResourceType_Opaque: + error = AwaObjectDefinition_AddResourceDefinitionAsOpaque(awaObject, + resource->id, resource->name, resource->isMandatory, + AwaResourceOperations_ReadWrite, + ((AwaOpaque){0})); + break; + + default: + LOG(LOG_ERR, "Unknown resource type found, can't be added to %s definition", object->name); + error = AwaError_Unspecified; + break; + } + + if (error != AwaError_Success) + { + LOG(LOG_ERR, "Failed to add %s resource to %s object definition\n" + "error: %s", resource->name, object->name, AwaError_ToString(error)); + result = false; + break; + } + } + + if (!result) + { + AwaObjectDefinition_Free(&awaObject); + return NULL; + } + + return awaObject; +} + +bool DefineObjectsAtServer(AwaServerSession *session, const OBJECT_T *objects, unsigned int numObjects) +{ + unsigned int i; + unsigned int definitionCount = 0; + bool result = true; + AwaError error = AwaError_Success; + const OBJECT_T *object; + + if (session == NULL) + { + LOG(LOG_ERR, "Null session passsed to %s()", __func__); + return false; + } + + LOG(LOG_INFO, "Registering objects"); + + AwaServerDefineOperation *handler = AwaServerDefineOperation_New(session); + if (handler == NULL) + { + LOG(LOG_ERR, "Failed to create define operation for session"); + return false; + } + + for (i = 0; i < numObjects; i++) + { + object = &objects[i]; + if (AwaServerSession_IsObjectDefined(session, object->id)) + { + LOG(LOG_DBG, "%s object already defined", object->name); + continue; + } + + AwaObjectDefinition *awaObject = CreateObjectDefinition(object); + if (awaObject == NULL) + { + LOG(LOG_ERR, "Failed to create %s definition", object->name); + result = false; + break; + } + + if ((error = AwaServerDefineOperation_Add(handler, awaObject)) != AwaError_Success) + { + LOG(LOG_ERR, "Failed to add %s definition to define operation\n" + "error: %s", object->name, AwaError_ToString(error)); + result = false; + AwaObjectDefinition_Free(&awaObject); + break; + } + definitionCount++; + AwaObjectDefinition_Free(&awaObject); + } + + if (result && definitionCount != 0) + { + if ((error = AwaServerDefineOperation_Perform(handler, IPC_TIMEOUT)) + != AwaError_Success) + { + LOG(LOG_ERR, "Failed to perform define operation\nerror: %s", AwaError_ToString(error)); + result = false; + } + } + if ((error = AwaServerDefineOperation_Free(&handler)) != AwaError_Success) + { + LOG(LOG_WARN, "Failed to free define operation object\nerror: %s", AwaError_ToString(error)); + } + return result; +} + + +bool DefineObjectsAtClient(AwaClientSession *session, const OBJECT_T *objects, unsigned int numObjects) +{ + unsigned int i; + unsigned int definitionCount = 0; + bool result = true; + AwaError error = AwaError_Success; + const OBJECT_T *object; + + if (session == NULL) + { + LOG(LOG_ERR, "Null session passsed to %s()", __func__); + return false; + } + + LOG(LOG_INFO, "Registering flow objects"); + + AwaClientDefineOperation *handler = AwaClientDefineOperation_New(session); + if (handler == NULL) + { + LOG(LOG_ERR, "Failed to create define operation for session"); + return false; + } + + for (i = 0; i < numObjects; i++) + { + object = &objects[i]; + + if (AwaClientSession_IsObjectDefined(session, object->id)) + { + LOG(LOG_DBG, "%s object already defined", object->name); + continue; + } + + AwaObjectDefinition *awaObject = CreateObjectDefinition(object); + if (awaObject == NULL) + { + LOG(LOG_ERR, "Failed to create %s definition", object->name); + result = false; + break; + } + + if ((error = AwaClientDefineOperation_Add(handler, awaObject)) != AwaError_Success) + { + LOG(LOG_ERR, "Failed to add %s definition to define operation\nerror: %s", object->name, AwaError_ToString(error)); + result = false; + } + definitionCount++; + AwaObjectDefinition_Free(&awaObject); + } + + if (result && definitionCount != 0) + { + if ((error = AwaClientDefineOperation_Perform(handler, IPC_TIMEOUT)) + != AwaError_Success) + { + LOG(LOG_ERR, "Failed to perform define operation\nerror: %s", AwaError_ToString(error)); + result = false; + } + } + if ((error = AwaClientDefineOperation_Free(&handler)) != AwaError_Success) + { + LOG(LOG_WARN, "Failed to free define operation object\nerror: %s", AwaError_ToString(error)); + } + return result; +} + +/** + * @brief Create specified resource if required, fill it with specified value and add it to set operation handler. + * @param[in] handler A pointer to set operation. + * @param[in] resourcePath The resource path requested for optional resource creation. + * @param[in] value Resource value to set. + * @param[in] type Resource type. + * @param[in] create_resource true if resource needs to be created + * @return true for success otherwise false. + */ +static bool AddResourceToHandler(AwaClientSetOperation *handler, const char *resourcePath, void *value, AwaResourceType type, bool create_resource) +{ + AwaInteger *intValue; + AwaOpaque *opaqueValue; + AwaTime *timeValue; + AwaError error; + + if (handler == NULL || resourcePath == NULL || value == NULL) + { + LOG(LOG_ERR, "Null params passed to %s()", __func__); + return false; + } + + LOG(LOG_DBG, "Add resource %s to set operation handler", resourcePath); + + if (create_resource) + { + if (AwaClientSetOperation_CreateOptionalResource(handler, resourcePath) + != AwaError_Success) + { + LOG(LOG_ERR, "Failed to create %s", resourcePath); + return false; + } + } + switch (type) + { + case AwaResourceType_String: + error = AwaClientSetOperation_AddValueAsCString(handler, resourcePath, (char *)value); + break; + + case AwaResourceType_Integer: + intValue = value; + error = AwaClientSetOperation_AddValueAsInteger(handler, resourcePath, *intValue); + break; + + case AwaResourceType_Time: + timeValue = value; + error = AwaClientSetOperation_AddValueAsTime(handler, resourcePath, *timeValue); + break; + + case AwaResourceType_Opaque: + opaqueValue = value; + error = AwaClientSetOperation_AddValueAsOpaque(handler, resourcePath, *opaqueValue); + break; + + default: + LOG(LOG_ERR, "Unknown resource type"); + return false; + } + + if (error != AwaError_Success) + { + LOG(LOG_ERR, "Failed to set value of %s\nerror: %s", resourcePath, AwaError_ToString(error)); + return false; + } + return true; +} + +bool SetResource(AwaClientSession *session, const char *resourcePath, void *value, AwaResourceType type) +{ + AwaError error; + bool result = false; + + if (session == NULL || resourcePath == NULL || value == NULL) + { + LOG(LOG_ERR, "Null params passed to %s()", __func__); + return false; + } + + LOG(LOG_DBG, "Setting value of %s", resourcePath); + + AwaClientSetOperation *handler = AwaClientSetOperation_New(session); + if (handler == NULL) + { + LOG(LOG_ERR, "Failed to create set operation for session"); + return false; + } + + if (AddResourceToHandler(handler, resourcePath, value, type, true)) + { + if ((error = AwaClientSetOperation_Perform(handler, IPC_TIMEOUT)) == AwaError_Success) + { + result = true; + } + else + { + LOG(LOG_ERR, "Failed to perform set operation\nerror: %s", AwaError_ToString(error)); + } + } + + if ((error = AwaClientSetOperation_Free(&handler)) != AwaError_Success) + { + LOG(LOG_WARN, "Failed to free set operation handler\nerror: %s", AwaError_ToString(error)); + } + return result; +} + +bool DoesObjectExist(AwaClientSession *session, AwaObjectID objectId, AwaObjectInstanceID objectInstanceId) +{ + const AwaClientGetResponse *response = NULL; + char objectInstancePath[URL_PATH_SIZE] = {0}; + bool result = false; + AwaError error; + + if (session == NULL) + { + LOG(LOG_ERR, "Null session passed to %s()", __func__); + return false; + } + + LOG(LOG_DBG, "Checking whether object %d exist or not", objectId); + + if ((error = AwaAPI_MakeObjectInstancePath(objectInstancePath, URL_PATH_SIZE, objectId, objectInstanceId)) != AwaError_Success) + { + LOG(LOG_ERR, "Failed to generate path for %d object\nerror: %s", objectId, AwaError_ToString(error)); + return false; + } + + AwaClientGetOperation *handler = AwaClientGetOperation_New(session); + if (handler == NULL) + { + LOG(LOG_ERR, "Failed to create get operation for session"); + return false; + } + + if ((error = AwaClientGetOperation_AddPath(handler, objectInstancePath)) == AwaError_Success) + { + if ((error = AwaClientGetOperation_Perform(handler, IPC_TIMEOUT)) == AwaError_Success) + { + response = AwaClientGetOperation_GetResponse(handler); + if (response != NULL) + { + if (AwaClientGetResponse_ContainsPath(response, objectInstancePath)) + { + result = true; + } + else + { + LOG(LOG_DBG, "%d object doesn't exist", objectId); + } + } + else + { + LOG(LOG_ERR, "Failed to get response from get operation handler"); + } + } + else + { + LOG(LOG_ERR, "Failed to perform get operation\nerror: %s", AwaError_ToString(error)); + } + } + else + { + LOG(LOG_ERR, "Failed to add %d object path to get operation handler\nerror: %s", objectId, AwaError_ToString(error)); + } + + if ((error = AwaClientGetOperation_Free(&handler)) != AwaError_Success) + { + LOG(LOG_WARN, "Failed to free get operation handler\nerror: %s", AwaError_ToString(error)); + } + return result; +} + +bool PopulateFlowObject(AwaClientSession *session, const char *deviceName, const char *deviceType, int64_t licenseeID, const char *fcap) +{ + char objectInstancePath[URL_PATH_SIZE] = {0}; + char deviceNameResourcePath[URL_PATH_SIZE] = {0}; + char deviceTypeResourcePath[URL_PATH_SIZE] = {0}; + char licenseeIdResourcePath[URL_PATH_SIZE] = {0}; + char fcapResourcePath[URL_PATH_SIZE] = {0}; + bool status = false; + bool create_resource = false; + AwaError error; + + if (session == NULL || deviceName == NULL || deviceType == NULL || fcap == NULL) + { + LOG(LOG_ERR, "Null parameters passed to %s()", __func__); + return false; + } + + LOG(LOG_INFO, "Populate flow object with device type, licensee id and fcap"); + + //Generate all object and resource paths + if ((error = MAKE_FLOW_OBJECT_INSTANCE_PATH(objectInstancePath) != AwaError_Success || + (error = MAKE_FLOW_OBJECT_RESOURCE_PATH(deviceNameResourcePath, + FlowObjectResourceId_DeviceName)) != AwaError_Success || + (error = MAKE_FLOW_OBJECT_RESOURCE_PATH(deviceTypeResourcePath, + FlowObjectResourceId_DeviceType)) != AwaError_Success || + (error = MAKE_FLOW_OBJECT_RESOURCE_PATH(licenseeIdResourcePath, + FlowObjectResourceId_LicenseeId)) != AwaError_Success || + (error = MAKE_FLOW_OBJECT_RESOURCE_PATH(fcapResourcePath, FlowObjectResourceId_Fcap)) + != AwaError_Success)) + { + LOG(LOG_ERR, "Failed to generate all object and resource paths\nerror: %s", AwaError_ToString(error)); + return false; + } + + AwaClientSetOperation *handler = AwaClientSetOperation_New(session); + if (handler == NULL) + { + LOG(LOG_ERR, "Failed to create set operation for session"); + return false; + } + + if (!DoesObjectExist(session, Lwm2mObjectId_FlowObject, OBJECT_INSTANCE_ID)) + { + LOG(LOG_DBG, "Flow object instance doesn't exist, so create it"); + if ((error = AwaClientSetOperation_CreateObjectInstance(handler, objectInstancePath)) != AwaError_Success) + { + LOG(LOG_ERR, "Failed to create flow object instance\nerror: %s", AwaError_ToString(error)); + } + create_resource = true; + } + else + { + LOG(LOG_DBG, "Flow object instance exist"); + } + + if (AddResourceToHandler(handler, deviceNameResourcePath, (void *)deviceName, + AwaResourceType_String, create_resource) && + AddResourceToHandler(handler, deviceTypeResourcePath, (void *)deviceType, + AwaResourceType_String, create_resource) && + AddResourceToHandler(handler, fcapResourcePath, (void *)fcap, AwaResourceType_String, + create_resource) && + AddResourceToHandler(handler, licenseeIdResourcePath, &licenseeID, + AwaResourceType_Integer, create_resource)) + { + if ((error = AwaClientSetOperation_Perform(handler, IPC_TIMEOUT)) == AwaError_Success) + { + status = true; + } + else + { + LOG(LOG_ERR, "Failed to perform set operation\nerror: %s", AwaError_ToString(error)); + } + } + else + { + LOG(LOG_ERR, "Failed to add flow object's resources(device name, device type, licensee id, fcap) to set operation handler"); + } + + if ((error = AwaClientSetOperation_Free(&handler)) != AwaError_Success) + { + LOG(LOG_WARN, "Failed to free set operation handler\nerror: %s", AwaError_ToString(error)); + } + return status; +} + +unsigned int GetResources(AwaClientSession *session, const OBJECT_T objects[], unsigned int numObjects, char strings[][MAX_STR_SIZE]) +{ + const char *value; + const AwaInteger *intValue; + const AwaTime *timeValue; + AwaOpaque opaqueValue; + unsigned int i, j, k, resCount = 0; + unsigned char *opaqueData; + char *offset; + const OBJECT_T *object; + RESOURCE_T *resource; + bool result = true; + AwaError error; + char objectInstancePath[URL_PATH_SIZE]; + char resourcePath[URL_PATH_SIZE]; + const AwaClientGetResponse *response; + AwaClientGetOperation *operation; + + if ((operation = AwaClientGetOperation_New(session)) == NULL) + { + LOG(LOG_ERR, "Failed to create get operation from session"); + return 0; + } + + for (i = 0; i < numObjects; i++) + { + memset(objectInstancePath, 0, URL_PATH_SIZE); + memset(resourcePath, 0, URL_PATH_SIZE); + + object = &objects[i]; + + if ((error = AwaAPI_MakeObjectInstancePath(objectInstancePath, URL_PATH_SIZE, + object->id, OBJECT_INSTANCE_ID)) != AwaError_Success) + { + LOG(LOG_ERR, "Failed to create path for %s object\nerror: %s", object->name, AwaError_ToString(error)); + result = false; + break; + } + if ((error = AwaClientGetOperation_AddPath(operation, objectInstancePath)) != AwaError_Success) + { + LOG(LOG_ERR, "Failed to add %s object path to get operation\nerror: %s", object->name, AwaError_ToString(error)); + result = false; + break; + } + if ((error = AwaClientGetOperation_Perform(operation, IPC_TIMEOUT)) != AwaError_Success) + { + LOG(LOG_ERR, "Failed to perform get operation for %s object\nerror: %s", object->name, AwaError_ToString(error)); + result = false; + break; + } + if ((response = AwaClientGetOperation_GetResponse(operation)) == NULL) + { + LOG(LOG_ERR, "Failed to get response from get operation for %s object", object->name); + result = false; + break; + } + if (!AwaClientGetResponse_ContainsPath(response, objectInstancePath)) + { + LOG(LOG_ERR, "Response doesn't contain %s object path", object->name); + result = false; + break; + } + + for (j = 0; j < object->numResources; j++) + { + resource = &object->resources[j]; + if (!resource->wantToSave) + { + continue; + } + if ((error = AwaAPI_MakeResourcePath(resourcePath, URL_PATH_SIZE, object->id, + OBJECT_INSTANCE_ID, resource->id)) != AwaError_Success) + { + LOG(LOG_ERR, "Failed to create path for %s resource\nerror: %s", resource->name, AwaError_ToString(error)); + result = false; + break; + } + if (!AwaClientGetResponse_HasValue(response, resourcePath)) + { + LOG(LOG_ERR, "Get operation response doesn't contain path of %s resource", resource->name); + result = false; + break; + } + + switch (resource->type) + { + case AwaResourceType_String: + if ((error = AwaClientGetResponse_GetValueAsCStringPointer(response, resourcePath, &value)) == AwaError_Success) + { + sprintf(strings[resCount++], "%s=\"%s\"", resource->name, value); + } + break; + + case AwaResourceType_Integer: + if ((error = AwaClientGetResponse_GetValueAsIntegerPointer(response, resourcePath, &intValue)) == AwaError_Success) + { + sprintf(strings[resCount++], "%s=\"%" PRId64 "\"", resource->name, *intValue); + } + break; + + case AwaResourceType_Time: + if ((error = AwaClientGetResponse_GetValueAsTimePointer(response, resourcePath, &timeValue)) == AwaError_Success) + { + sprintf(strings[resCount++], "%s=\"%" PRId64 "\"", resource->name, *timeValue); + } + break; + + case AwaResourceType_Opaque: + if ((error = AwaClientGetResponse_GetValueAsOpaque(response, resourcePath, &opaqueValue)) == AwaError_Success) + { + opaqueData = (unsigned char *)opaqueValue.Data; + offset = strings[resCount]; + offset += sprintf(strings[resCount], "%s=\"", resource->name); + for (k = 0; k < DEVICE_ID_SIZE; k++) + { + offset += sprintf(offset, "%02X ", *opaqueData++); + } + sprintf(offset, "\""); + resCount++; + } + break; + + default: + LOG(LOG_ERR, "Unknown resource type"); + error = AwaError_Unspecified; + break; + } + if (error != AwaError_Success) + { + LOG(LOG_ERR, "Failed to get %s resource value from response\nerror: %s", resource->name, AwaError_ToString(error)); + result = false; + break; + } + } + if (!result) + { + break; + } + } + + if ((error = AwaClientGetOperation_Free(&operation)) != AwaError_Success) + { + LOG(LOG_WARN, "Failed to free get operation\nerror: %s", AwaError_ToString(error)); + } + return (result ? resCount : 0); +} diff --git a/src/fdm_register.h b/src/fdm_register.h new file mode 100644 index 0000000..80bbdae --- /dev/null +++ b/src/fdm_register.h @@ -0,0 +1,103 @@ +/*************************************************************************************************** + * Copyright (c) 2016, Imagination Technologies Limited and/or its affiliated group companies + * and/or licensors + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used to + * endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY + * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @file fdm_register.h + * @brief Header file for exposing operations for registering flow and flow access objects, get and + * set their resources and check their existence. + */ + +#ifndef FDM_REGISTER_H +#define FDM_REGISTER_H + +#include "awa/client.h" +#include "awa/server.h" +#include "awa/common.h" +#include "fdm_common.h" + +/** + * @brief Define objects and their resources and register them with Awa client. + * @param[in] session A pointer to a valid session with Awa client. + * @param[in] objects Pointer to array of objects to be defined and registered + * @param[in] numObjects Number of objects to define and register. + * @return true for success otherwise false. + */ +bool DefineObjectsAtClient(AwaClientSession *session, const OBJECT_T *objects, unsigned int numObjects); + +/** + * @brief Define objects and their resources and register them with Awa server. + * @param[in] session A pointer to a valid session with Awa server. + * @param[in] objects Pointer to array of objects to be defined and registered + * @param[in] numObjects Number of objects to define and register. + * @return true for success otherwise false. + */ +bool DefineObjectsAtServer(AwaServerSession *session, const OBJECT_T *objects, unsigned int numObjects); + +/** + * @brief Check lwm2m object existence. + * @param[in] session A pointer to a valid session. + * @param[in] objectId Identifies the object for which the query is targeted. + * @param[in] objectInstanceId The numerical object instance id. + * @return true for success otherwise false. + */ +bool DoesObjectExist(AwaClientSession *session, AwaObjectID objectId, AwaObjectInstanceID objectInstanceId); + +/** + * @brief Populate flow object with device type, licensee id and fcap. + * @param[in] session A pointer to a valid session. + * @param[in] deviceName User assigned name of device. + * @param[in] deviceType FlowCloud registered device type. + * @param[in] licenseeID Licensee. + * @param[in] fcap FlowCloud Access Provisioning Code. + * @return true for success otherwise false. + */ +bool PopulateFlowObject(AwaClientSession *session, const char *deviceName, const char *deviceType, int64_t licenseeID, const char *fcap); + +/** + * @brief Set specified value to resource. + * @param[in] session A pointer to a valid session. + * @param[in] resourcePath Resource path to set value. + * @param[in] value Resource value to set. + * @param[in] type Resource type. + * @return true for success otherwise false. + */ +bool SetResource(AwaClientSession *session, const char *resourcePath, void *value, AwaResourceType type); + +/** + * @brief Get value of specified object's resources for which wantToSave parameter is set. + * @param[in] session A pointer to a valid session. + * @param[in] objects Flow object's properties. + * @param[in] numObjects Number of objects to define and register. + * @param[out] strings Resource value strings. + * @return resource count for success and 0 for fail. + */ +unsigned int GetResources(AwaClientSession *session, const OBJECT_T objects[], unsigned int numObjects, char strings[][MAX_STR_SIZE]); + +#endif /* FDM_REGISTER_H */ diff --git a/src/fdm_server_session.c b/src/fdm_server_session.c new file mode 100644 index 0000000..9481f38 --- /dev/null +++ b/src/fdm_server_session.c @@ -0,0 +1,95 @@ +/*************************************************************************************************** + * Copyright (c) 2016, Imagination Technologies Limited and/or its affiliated group companies + * and/or licensors + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used to + * endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY + * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @file fdm_server_session.c + * @brief Defines methods to establish and release sessions with Awa LWM2M server. + */ + +/*************************************************************************************************** + * Includes + **************************************************************************************************/ + +#include "awa/server.h" +#include "fdm_log.h" + +/*************************************************************************************************** + * Methods + **************************************************************************************************/ + +AwaServerSession *Server_EstablishSession(const char *address, unsigned int port) +{ + // Initialise Device Management session + AwaServerSession *session; + session = AwaServerSession_New(); + + if (session == NULL) + { + LOG(LOG_ERR, "Failed to create new server session"); + return NULL; + } + + if (AwaServerSession_SetIPCAsUDP(session, address, port) != AwaError_Success) + { + LOG(LOG_ERR, "Failed to set IPC as UDP for server session"); + AwaServerSession_Free(&session); + return NULL; + } + + + if (AwaServerSession_Connect(session) == AwaError_Success) + { + LOG(LOG_INFO, "Session established with server"); + } + else + { + LOG(LOG_ERR, "Failed to establish session with server"); + AwaServerSession_Free(&session); + } + return session; +} + +void Server_ReleaseSession(AwaServerSession **session) +{ + if (session == NULL) + { + return; + } + + if (AwaServerSession_Disconnect(*session) != AwaError_Success) + { + LOG(LOG_ERR, "Failed to disconnect session with server"); + } + + if (AwaServerSession_Free(session) != AwaError_Success) + { + LOG(LOG_ERR, "Failed to free session with server"); + } +} diff --git a/src/fdm_server_session.h b/src/fdm_server_session.h new file mode 100644 index 0000000..6cb5671 --- /dev/null +++ b/src/fdm_server_session.h @@ -0,0 +1,55 @@ +/*************************************************************************************************** + * Copyright (c) 2016, Imagination Technologies Limited and/or its affiliated group companies + * and/or licensors + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used to + * endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY + * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @file fdm_server_session.h + * @brief Header file for exposing operations to establish and release sessions with Awa LWM2M server. + */ + +#ifndef FDM_SERVER_SESSION_H +#define FDM_SERVER_SESSION_H + +#include "fdm_common.h" + +/** + * @brief Establish a session with Awa LWM2M Server + * @param[in] address IP address of the server + * @param[in] port port of the server + * @return a pointer to session with server. + */ +AwaServerSession *Server_EstablishSession(const char *address, unsigned int port); + +/** + * @brief Release a session with Awa LWM2M Server + * @param[in] session A pointer to a valid session. + */ +void Server_ReleaseSession(AwaServerSession **session); + +#endif /* FDM_SERVER_SESSION_H */ diff --git a/src/fdm_subscribe.c b/src/fdm_subscribe.c new file mode 100644 index 0000000..57671bd --- /dev/null +++ b/src/fdm_subscribe.c @@ -0,0 +1,407 @@ +/*************************************************************************************************** + * Copyright (c) 2016, Imagination Technologies Limited and/or its affiliated group companies + * and/or licensors + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used to + * endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY + * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @file fdm_subscribe.c + * @brief Provides subscribe, unsubscribe operations and callbacks for object change notifications. + */ + +/*************************************************************************************************** + * Includes + **************************************************************************************************/ + +#include +#include +#include +#include "fdm_common.h" +#include "fdm_log.h" + +/*************************************************************************************************** + * Methods + **************************************************************************************************/ + +/** +* @brief A user-specified callback handler for a Change Subscription which will be fired on +* AwaClientSession_DispatchCallbacks if the subscribed entity (flow object) has changed +* since the subscription's session callbacks were last dispatched. +* @param[in] changeSet A pointer to a valid ChangeSet. +* @param[in] context A pointer to user-specified data passed to AwaClientChangeSubscription_New. +*/ +static void flowObjectCallback(const AwaChangeSet *changeSet, void *context) +{ + static bool done = false; + char licenseeChallengeResourcePath[URL_PATH_SIZE] = {0}; + char hashIterationsResourcePath[URL_PATH_SIZE] = {0}; + AwaOpaque licenseeChallenge = {0}; + Verification *verificationData = (Verification *)context; + AwaError error; + + if (changeSet == NULL || context == NULL) + { + LOG(LOG_DBG, "Flow object change notification doesn't contain any data"); + return; + } + + LOG(LOG_INFO, "Flow object updated"); + + // Extract and store licensee challenge + if ((error = MAKE_FLOW_OBJECT_RESOURCE_PATH(licenseeChallengeResourcePath, FlowObjectResourceId_LicenseeChallenge)) == AwaError_Success) + { + if (AwaChangeSet_ContainsPath(changeSet, licenseeChallengeResourcePath)) + { + if ((error = AwaChangeSet_GetValueAsOpaque(changeSet, licenseeChallengeResourcePath, + &licenseeChallenge)) == AwaError_Success && licenseeChallenge.Data != NULL && licenseeChallenge.Size > 0) + { + if (verificationData->challenge.Data != NULL) + { + free(verificationData->challenge.Data); + } + + verificationData->challenge.Data = malloc(licenseeChallenge.Size); + + if (verificationData->challenge.Data != NULL) + { + memcpy(verificationData->challenge.Data, licenseeChallenge.Data, licenseeChallenge.Size); + verificationData->challenge.Size = licenseeChallenge.Size; + verificationData->hasChallenge = true; + } + else + { + LOG(LOG_ERR, "Failed to allocate memory for storing licensee challenge"); + } + } + else + { + LOG(LOG_ERR, "Failed to get licensee challenge\nerror: %s", AwaError_ToString(error)); + } + } + else + { + LOG(LOG_DBG, "Flow object change notification doesn't contain licensee challenge resource"); + } + } + else + { + LOG(LOG_DBG, "Failed to create licensee challenge resource path\nerror: %s", AwaError_ToString(error)); + } + + if ((error = MAKE_FLOW_OBJECT_RESOURCE_PATH(hashIterationsResourcePath, FlowObjectResourceId_HashIterations)) == AwaError_Success) + { + if (AwaChangeSet_ContainsPath(changeSet, hashIterationsResourcePath)) + { + const AwaInteger *iterationsValue = NULL; + if (AwaChangeSet_GetValueAsIntegerPointer(changeSet, hashIterationsResourcePath, &iterationsValue) == AwaError_Success) + { + verificationData->iterations = *iterationsValue; + verificationData->hasIterations = true; + } + else + { + LOG(LOG_ERR, "Failed to get hash iterations"); + } + } + else + { + LOG(LOG_DBG, "Flow object change notification doesn't contain hash iterations resource"); + } + } + else + { + LOG(LOG_DBG, "Failed to create hash iterations resource path\nerror: %s", AwaError_ToString(error)); + } + + // no errors yet, check to see if we have what we need for provisioning + if (verificationData->waitForServerResponse && verificationData->hasChallenge && verificationData->hasIterations && !done) + { + verificationData->verifyLicensee = true; + done = true; // do this step once, because setting an object that we are observing will cause an infinite loop + } +} + +/** +* @brief Check whether ChangeSet contains specified resource and also whether that resource contains any value. +* @param[in] changeSet A pointer to a valid ChangeSet. +* @param[in] resourcePath The resource path with which to query the Get Response. +* @return true for success otherwise false. +*/ +static bool HasResource(const AwaChangeSet *changeSet, const char *resourcePath) +{ + if (changeSet == NULL || resourcePath == NULL) + { + LOG(LOG_ERR, "Null params passed to %s()", __func__); + return false; + } + + return (AwaChangeSet_ContainsPath(changeSet, resourcePath) && AwaChangeSet_HasValue(changeSet, resourcePath)); +} + +/** +* @brief A user-specified callback handler for a Change Subscription which will be fired on +* AwaClientSession_DispatchCallbacks if the subscribed entity (flow access object) has +* changed since the subscription's session callbacks were last dispatched. +* @param[in] changeSet A pointer to a valid ChangeSet. +* @param[in] context A pointer to user-specified data passed to AwaClientChangeSubscription_New. + */ +static void flowAccessCallback(const AwaChangeSet *changeSet, void *context) +{ + char urlPath[URL_PATH_SIZE] = {0}; + char keyPath[URL_PATH_SIZE] = {0}; + char secretPath[URL_PATH_SIZE] = {0}; + char tokenPath[URL_PATH_SIZE] = {0}; + char tokenExpiryPath[URL_PATH_SIZE] = {0}; + Verification *verificationData = (Verification *)context; + AwaError error; + + if (changeSet == NULL || context == NULL) + { + return; + } + + LOG(LOG_INFO, "Flow access object updated"); + + if ((error = MAKE_FLOW_ACCESS_OBJECT_RESOURCE_PATH(urlPath, FlowAccessResourceId_Url)) + != AwaError_Success || + (error = MAKE_FLOW_ACCESS_OBJECT_RESOURCE_PATH(keyPath, FlowAccessResourceId_CustomerKey)) + != AwaError_Success || + (error = MAKE_FLOW_ACCESS_OBJECT_RESOURCE_PATH(secretPath, + FlowAccessResourceId_CustomerSecret)) != AwaError_Success || + (error = MAKE_FLOW_ACCESS_OBJECT_RESOURCE_PATH(tokenPath, + FlowAccessResourceId_RememberMeToken)) != AwaError_Success || + (error = MAKE_FLOW_ACCESS_OBJECT_RESOURCE_PATH(tokenExpiryPath, + FlowAccessResourceId_RememberMeTokenExpiry)) != AwaError_Success) + { + LOG(LOG_ERR, "Failed to generate resource path for all Flow access resources\nerror: %s", AwaError_ToString(error)); + return; + } + + if (!HasResource(changeSet, urlPath) || + !HasResource(changeSet, keyPath) || + !HasResource(changeSet, secretPath) || + !HasResource(changeSet, tokenPath) || + !HasResource(changeSet, tokenExpiryPath)) + { + LOG(LOG_ERR, "Flow access notification doesn't have all the resources"); + verificationData->waitForServerResponse = false; + return; + } + + LOG(LOG_INFO, "Gateway device provisioned successfully"); + verificationData->waitForServerResponse = false; + verificationData->isProvisionSuccess = true; +} + +bool SubscribeToFlowObjects(AwaClientSession *session, FlowSubscriptions *subscriptions, Verification *verificationData) +{ + char flowObjectInstancePath[URL_PATH_SIZE] = {0}; + char flowAccessInstancePath[URL_PATH_SIZE] = {0}; + const AwaClientSubscribeResponse *response; + const AwaPathResult *flowObjectSubscribeResult; + const AwaPathResult *flowAccessSubscribeResult; + bool result = false; + AwaError error; + + if(session == NULL || subscriptions == NULL || verificationData == NULL) + { + LOG(LOG_ERR, "Null params passed to %s()", __func__); + return false; + } + + LOG(LOG_INFO, "Subscribing to Flow and Flow Access object change notifications"); + + if ((error = MAKE_FLOW_OBJECT_INSTANCE_PATH(flowObjectInstancePath)) != AwaError_Success || + (error = MAKE_FLOW_ACCESS_OBJECT_PATH(flowAccessInstancePath)) != AwaError_Success) + { + LOG(LOG_ERR, "Failed to generate path for %u or %u objects\nerror: %s", + Lwm2mObjectId_FlowObject, Lwm2mObjectId_FlowAccess, AwaError_ToString(error)); + return false; + } + + // Subscribe to Flow and Flow access object change notifications and the specified callback + // function will be fired on AwaClientSession_DispatchCallbacks if the subscribed entity has + // changed since the session callbacks were last dispatched. + subscriptions->flowObjectChange = AwaClientChangeSubscription_New(flowObjectInstancePath, flowObjectCallback, verificationData); + if (subscriptions->flowObjectChange == NULL) + { + LOG(LOG_ERR, "Failed to create flow subscription object"); + return false; + } + subscriptions->flowAccessObjectChange = AwaClientChangeSubscription_New(flowAccessInstancePath, flowAccessCallback, verificationData); + if (subscriptions->flowAccessObjectChange == NULL) + { + LOG(LOG_ERR, "Failed to create flow access subscription object"); + return false; + } + + AwaClientSubscribeOperation *operation = AwaClientSubscribeOperation_New(session); + if (operation == NULL) + { + LOG(LOG_ERR, "Failed to create subscribe operation from session"); + return false; + } + + if ((error = AwaClientSubscribeOperation_AddChangeSubscription(operation, subscriptions->flowObjectChange)) == AwaError_Success && + (error = AwaClientSubscribeOperation_AddChangeSubscription(operation, subscriptions->flowAccessObjectChange)) == AwaError_Success) + { + if ((error = AwaClientSubscribeOperation_Perform(operation, IPC_TIMEOUT)) == AwaError_Success) + { + response = AwaClientSubscribeOperation_GetResponse(operation); + if (((flowObjectSubscribeResult = AwaClientSubscribeResponse_GetPathResult(response, flowObjectInstancePath)) != NULL) && + ((flowAccessSubscribeResult = AwaClientSubscribeResponse_GetPathResult(response, flowAccessInstancePath)) != NULL)) + { + if ((error = AwaPathResult_GetError(flowObjectSubscribeResult)) == AwaError_Success && + (error = AwaPathResult_GetError(flowAccessSubscribeResult)) == AwaError_Success) + { + result = true; + } + else + { + LOG(LOG_ERR, "Failed to get error from subscribe result for flow object or " + "flow access object or both\nerror: %s", AwaError_ToString(error)); + } + } + else + { + LOG(LOG_ERR, "Failed to get flow object or flow access object path in subscribe operation response"); + } + } + else + { + LOG(LOG_ERR, "Failed to perform subscribe operation\nerror: %s", AwaError_ToString(error)); + } + } + else + { + LOG(LOG_ERR, "Failed to add change subscription to subscribe operation of flow object or " + "flow access object or both\nerror: %s", AwaError_ToString(error)); + } + + if ((error = AwaClientSubscribeOperation_Free(&operation)) != AwaError_Success) + { + LOG(LOG_ERR, "Failed to free subscribe operation\nerror: %s", AwaError_ToString(error)); + } + return result; +} + +void UnSubscribeFromFlowObjects(AwaClientSession *session, FlowSubscriptions *subscriptions) +{ + AwaClientSubscribeOperation *operation; + const AwaClientSubscribeResponse *response; + const AwaPathResult *flowSubscribeResult; + const AwaPathResult *flowAccessSubscribeResult; + char flowObjectInstancePath[URL_PATH_SIZE]; + char flowAccessPath[URL_PATH_SIZE]; + bool result = true; + AwaError error; + + if(session == NULL || subscriptions == NULL) + { + LOG(LOG_ERR, "Null params passed to %s()", __func__); + return; + } + + LOG(LOG_INFO, "Unsubscribe from flow and flow access change notifications"); + + if ((error = MAKE_FLOW_OBJECT_INSTANCE_PATH(flowObjectInstancePath)) != AwaError_Success || + (error = MAKE_FLOW_ACCESS_OBJECT_PATH(flowAccessPath)) != AwaError_Success) + { + LOG(LOG_ERR, "Failed to create path for flow object or flow access object\nerror: %s", AwaError_ToString(error)); + result = false; + } + + if (result && ((operation = AwaClientSubscribeOperation_New(session)) == NULL)) + { + LOG(LOG_ERR, "Failed to create subscribe operation from session"); + result = false; + } + + if (result && + (error = AwaClientSubscribeOperation_AddCancelChangeSubscription(operation, + subscriptions->flowObjectChange)) == AwaError_Success && + (error = AwaClientSubscribeOperation_AddCancelChangeSubscription(operation, + subscriptions->flowAccessObjectChange)) == AwaError_Success) + { + if ((error = AwaClientSubscribeOperation_Perform(operation, IPC_TIMEOUT)) == AwaError_Success) + { + response = AwaClientSubscribeOperation_GetResponse(operation); + if (((flowSubscribeResult = AwaClientSubscribeResponse_GetPathResult(response, flowObjectInstancePath)) != NULL) && + (flowAccessSubscribeResult = AwaClientSubscribeResponse_GetPathResult(response, flowAccessPath)) != NULL) + { + if ((error = AwaPathResult_GetError(flowSubscribeResult)) == AwaError_Success && + (error = AwaPathResult_GetError(flowAccessSubscribeResult)) == AwaError_Success) + { + LOG(LOG_DBG, "Successfully cancelled subscription to flow and flow access update events"); + } + else + { + LOG(LOG_ERR, "Failed to cancel subscription to flow object or flow access " + "object update events or both\nerror: %s", AwaError_ToString(error)); + } + } + else + { + LOG(LOG_ERR, "Failed to get flow object or flow acccess object path from subscribe operation response"); + } + } + else + { + LOG(LOG_ERR, "Failed to perform subscribe operation for flow object or flow access object or both\nerror: %s", AwaError_ToString(error)); + } + } + else + { + LOG(LOG_ERR, "Failed to add cancel flag to a change subscription in a specified subscribe" + "operation for flow object or flow access object or both\nerror: %s", + AwaError_ToString(error)); + } + + if (subscriptions->flowObjectChange != NULL) + { + if ((error = AwaClientChangeSubscription_Free(&subscriptions->flowObjectChange)) != AwaError_Success) + { + LOG(LOG_ERR, "Failed to free flow subscription object\nerror: %s", AwaError_ToString(error)); + } + } + + if (subscriptions->flowAccessObjectChange != NULL) + { + if ((error = AwaClientChangeSubscription_Free(&subscriptions->flowAccessObjectChange)) != AwaError_Success) + { + LOG(LOG_ERR, "Failed to free flow access subscription object\nerror: %s", AwaError_ToString(error)); + } + } + + if (operation != NULL) + { + if ((error = AwaClientSubscribeOperation_Free(&operation)) != AwaError_Success) + { + LOG(LOG_ERR, "Failed to free subscribe operation\nerror: %s", AwaError_ToString(error)); + } + } +} diff --git a/src/fdm_subscribe.h b/src/fdm_subscribe.h new file mode 100644 index 0000000..4617998 --- /dev/null +++ b/src/fdm_subscribe.h @@ -0,0 +1,60 @@ +/*************************************************************************************************** + * Copyright (c) 2016, Imagination Technologies Limited and/or its affiliated group companies + * and/or licensors + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used to + * endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY + * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @file fdm_subscribe.h + * @brief Header file for exposing subscribe and unsubscribe operations of lwm2m objects. + */ + +#ifndef FDM_SUBSCRIBE_H +#define FDM_SUBSCRIBE_H + +#include "fdm_common.h" + +/** + * @brief Send a request to the Awa LWM2M Core to create a Change Subscription for flow and + * flow access to the specified subscriptions parameter. + * @param[in] session A pointer to a valid session. + * @param[in] subscriptions A pointer to valid Change Subscriptions. + * @param[in] verificationData Licensee verification data. + * @return true for success otherwise false. + */ +bool SubscribeToFlowObjects(AwaClientSession *session, FlowSubscriptions *subscriptions, Verification *verificationData); + +/** + * @brief Send a request to the Awa LWM2M Core to remove the Change Subscriptions for flow and + * flow access objects represented by the subscriptions parameter and shut down the change + * change subscriptions, freeing any allocated memory. + * @param[in] session A pointer to a valid session. + * @param[in] subscriptions A pointer to valid Change Subscriptions. + */ +void UnSubscribeFromFlowObjects(AwaClientSession *session, FlowSubscriptions *subscriptions); + +#endif /* FDM_SUBSCRIBE_H */ diff --git a/src/utils.c b/src/utils.c new file mode 100644 index 0000000..7151e39 --- /dev/null +++ b/src/utils.c @@ -0,0 +1,57 @@ +/*************************************************************************************************** + * Copyright (c) 2016, Imagination Technologies Limited and/or its affiliated group companies + * and/or licensors + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used to + * endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY + * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + /** + * @file utils.c + * @brief Provides common functions. + */ + +/*************************************************************************************************** + * Implementation + **************************************************************************************************/ + + /** +* @brief Copy source string to target without space +*/ +void CopyStringWithoutSpace(char *targetString, const char *sourceString) +{ + unsigned int i = 0, j = 0; + + while (sourceString[i] != '\0') + { + if (sourceString[i] != ' ') + { + targetString[j] = sourceString[i]; + j++; + } + i++; + } + targetString[j] = '\0'; +} diff --git a/src/utils.h b/src/utils.h new file mode 100644 index 0000000..72de39f --- /dev/null +++ b/src/utils.h @@ -0,0 +1,52 @@ +/*************************************************************************************************** + * Copyright (c) 2016, Imagination Technologies Limited and/or its affiliated group companies + * and/or licensors + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used to + * endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY + * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + /** + * @file utils.h + * @brief Header file for exposing utils functions. + */ + +#ifndef UTILS_H +#define UTILS_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Copy source string to target without space + */ +void CopyStringWithoutSpace(char *targetString, const char *sourceString); + +#ifdef __cplusplus +} +#endif + +#endif /* UTILS_H */