diff --git a/examples/mms_utility/mms_utility.c b/examples/mms_utility/mms_utility.c index f505348e..0d1e9753 100644 --- a/examples/mms_utility/mms_utility.c +++ b/examples/mms_utility/mms_utility.c @@ -18,6 +18,7 @@ print_help() printf("-i show server identity\n"); printf("-t show domain directory\n"); printf("-r read domain variable\n"); + printf("-c specify component name for variable read\n"); printf("-a specify domain for read or write command\n"); printf("-f show file list\n"); printf("-g get file attributes\n"); @@ -105,6 +106,7 @@ int main(int argc, char** argv) { char* domainName = NULL; char* variableName = NULL; + char* componentName = NULL; char* filename = NULL; char* journalName = NULL; @@ -122,7 +124,7 @@ int main(int argc, char** argv) { int c; - while ((c = getopt(argc, argv, "mifdh:p:l:t:a:r:g:j:x:v:")) != -1) + while ((c = getopt(argc, argv, "mifdh:p:l:t:a:r:g:j:x:v:c:")) != -1) switch (c) { case 'm': printRawMmsMessages = 1; @@ -155,6 +157,9 @@ int main(int argc, char** argv) { readVariable = 1; variableName = StringUtils_copyString(optarg); break; + case 'c': + componentName = StringUtils_copyString(optarg); + break; case 'v': readVariableList = 1; variableName = StringUtils_copyString(optarg); @@ -323,7 +328,13 @@ int main(int argc, char** argv) { if (readVariable) { if (readWriteHasDomain) { - MmsValue* result = MmsConnection_readVariable(con, &error, domainName, variableName); + + MmsValue* result; + + if (componentName == NULL) + result = MmsConnection_readVariable(con, &error, domainName, variableName); + else + result = MmsConnection_readVariableComponent(con, &error, domainName, variableName, componentName); if (error != MMS_ERROR_NONE) { printf("Reading variable failed: (ERROR %i)\n", error); diff --git a/src/mms/inc/mms_client_connection.h b/src/mms/inc/mms_client_connection.h index f4d631b3..a4a7e837 100644 --- a/src/mms/inc/mms_client_connection.h +++ b/src/mms/inc/mms_client_connection.h @@ -373,6 +373,23 @@ MmsConnection_getVariableListNamesAssociationSpecific(MmsConnection self, MmsErr MmsValue* MmsConnection_readVariable(MmsConnection self, MmsError* mmsError, const char* domainId, const char* itemId); +/** + * \brief Read a component of a single variable from the server. + * + * \param self MmsConnection instance to operate on + * \param mmsError user provided variable to store error code + * \param domainId the domain name of the variable to be read or NULL to read a VMD specific named variable + * \param itemId name of the variable to be read + * \param componentId the component name + * + * \return Returns a MmsValue object or NULL if the request failed. The MmsValue object can + * either be a simple value or a complex value or array. It is also possible that the return value is NULL + * even if mmsError = MMS_ERROR_NON. This is the case when the servers returns an empty result list. + */ +MmsValue* +MmsConnection_readVariableComponent(MmsConnection self, MmsError* mmsError, + const char* domainId, const char* itemId, const char* componentId); + /** * \brief Read one or more elements of a single array variable from the server. * diff --git a/src/mms/inc_private/mms_client_internal.h b/src/mms/inc_private/mms_client_internal.h index 9ffdb731..6b53a5e4 100644 --- a/src/mms/inc_private/mms_client_internal.h +++ b/src/mms/inc_private/mms_client_internal.h @@ -160,6 +160,9 @@ mmsClient_parseReadResponse(ByteBuffer* message, uint32_t* invokeId, bool create int mmsClient_createReadRequest(uint32_t invokeId, const char* domainId, const char* itemId, ByteBuffer* writeBuffer); +int +mmsClient_createReadRequestComponent(uint32_t invokeId, const char* domainId, const char* itemId, const char* component, ByteBuffer* writeBuffer); + int mmsClient_createReadRequestAlternateAccessIndex(uint32_t invokeId, const char* domainId, const char* itemId, uint32_t index, uint32_t elementCount, ByteBuffer* writeBuffer); diff --git a/src/mms/inc_private/mms_server_internal.h b/src/mms/inc_private/mms_server_internal.h index 7a9155f4..eef28d1f 100644 --- a/src/mms/inc_private/mms_server_internal.h +++ b/src/mms/inc_private/mms_server_internal.h @@ -357,9 +357,13 @@ mmsServer_handleObtainFileRequest( uint32_t invokeId, ByteBuffer* response); -int + +bool mmsServer_isIndexAccess(AlternateAccess_t* alternateAccess); +bool +mmsServer_isComponentAccess(AlternateAccess_t* alternateAccess); + int mmsServer_getLowIndex(AlternateAccess_t* alternateAccess); diff --git a/src/mms/iso_mms/client/mms_client_connection.c b/src/mms/iso_mms/client/mms_client_connection.c index b93ef4f3..d88aba2f 100644 --- a/src/mms/iso_mms/client/mms_client_connection.c +++ b/src/mms/iso_mms/client/mms_client_connection.c @@ -1589,6 +1589,34 @@ MmsConnection_readVariable(MmsConnection self, MmsError* mmsError, return value; } +MmsValue* +MmsConnection_readVariableComponent(MmsConnection self, MmsError* mmsError, + const char* domainId, const char* itemId, const char* componentId) +{ + MmsValue* value = NULL; + + if (getAssociationState(self) != MMS_STATE_CONNECTED) { + *mmsError = MMS_ERROR_CONNECTION_LOST; + goto exit_function; + } + + ByteBuffer* payload = IsoClientConnection_allocateTransmitBuffer(self->isoClient); + + uint32_t invokeId = getNextInvokeId(self); + + mmsClient_createReadRequestComponent(invokeId, domainId, itemId, componentId, payload); + + ByteBuffer* responseMessage = sendRequestAndWaitForResponse(self, invokeId, payload, mmsError); + + if (responseMessage != NULL) + value = mmsClient_parseReadResponse(self->lastResponse, NULL, false); + + releaseResponse(self); + + exit_function: + return value; +} + MmsValue* MmsConnection_readArrayElements(MmsConnection self, MmsError* mmsError, const char* domainId, const char* itemId, diff --git a/src/mms/iso_mms/client/mms_client_read.c b/src/mms/iso_mms/client/mms_client_read.c index 2a29812e..fbd97672 100644 --- a/src/mms/iso_mms/client/mms_client_read.c +++ b/src/mms/iso_mms/client/mms_client_read.c @@ -528,7 +528,7 @@ mmsClient_createReadRequest(uint32_t invokeId, const char* domainId, const char* } static AlternateAccess_t* -createAlternateAccess(uint32_t index, uint32_t elementCount) +createAlternateAccessComponent(const char* componentName) { AlternateAccess_t* alternateAccess = (AlternateAccess_t*) GLOBAL_CALLOC(1, sizeof(AlternateAccess_t)); alternateAccess->list.count = 1; @@ -538,37 +538,114 @@ createAlternateAccess(uint32_t index, uint32_t elementCount) alternateAccess->list.array[0]->choice.unnamed = (AlternateAccessSelection_t*) GLOBAL_CALLOC(1, sizeof(AlternateAccessSelection_t)); - alternateAccess->list.array[0]->choice.unnamed->present = AlternateAccessSelection_PR_selectAccess; - - if (elementCount > 0) { - alternateAccess->list.array[0]->choice.unnamed->choice.selectAccess.present = - AlternateAccessSelection__selectAccess_PR_indexRange; + const char* separator = strchr(componentName, '$'); - INTEGER_t* asnIndex = - &(alternateAccess->list.array[0]->choice.unnamed->choice.selectAccess.choice.indexRange.lowIndex); + if (separator) { + int size = separator - componentName; - asn_long2INTEGER(asnIndex, index); + alternateAccess->list.array[0]->choice.unnamed->present = AlternateAccessSelection_PR_selectAlternateAccess; + alternateAccess->list.array[0]->choice.unnamed->choice.selectAlternateAccess.accessSelection.present = + AlternateAccessSelection__selectAlternateAccess__accessSelection_PR_component; - asnIndex = - &(alternateAccess->list.array[0]->choice.unnamed->choice.selectAccess.choice.indexRange.numberOfElements); + alternateAccess->list.array[0]->choice.unnamed->choice.selectAlternateAccess.accessSelection.choice.component.buf = + (uint8_t*) StringUtils_copySubString((char*) componentName, (char*) separator); + alternateAccess->list.array[0]->choice.unnamed->choice.selectAlternateAccess.accessSelection.choice.component.size = size; - asn_long2INTEGER(asnIndex, elementCount); + alternateAccess->list.array[0]->choice.unnamed->choice.selectAlternateAccess.alternateAccess = createAlternateAccessComponent(separator + 1); } else { - alternateAccess->list.array[0]->choice.unnamed->choice.selectAccess.present = - AlternateAccessSelection__selectAccess_PR_index; + int size = strlen(componentName); - INTEGER_t* asnIndex = - &(alternateAccess->list.array[0]->choice.unnamed->choice.selectAccess.choice.index); + alternateAccess->list.array[0]->choice.unnamed->present = AlternateAccessSelection_PR_selectAccess; - asn_long2INTEGER(asnIndex, index); + alternateAccess->list.array[0]->choice.unnamed->choice.selectAccess.present = + AlternateAccessSelection__selectAccess_PR_component; + + alternateAccess->list.array[0]->choice.unnamed->choice.selectAccess.choice.component.buf = + (uint8_t*) StringUtils_copyString(componentName); + alternateAccess->list.array[0]->choice.unnamed->choice.selectAccess.choice.component.size = size; } return alternateAccess; } +static void +deleteAlternateAccessComponent(AlternateAccess_t* alternateAccess) +{ + GLOBAL_FREEMEM(alternateAccess->list.array[0]->choice.unnamed); + GLOBAL_FREEMEM(alternateAccess->list.array[0]); + GLOBAL_FREEMEM(alternateAccess->list.array); + GLOBAL_FREEMEM(alternateAccess); +} + +static ListOfVariableSeq_t* +createNewVariableSpecification(const char* domainId, const char* itemId, const char* componentName, bool associationSpecific) +{ + ListOfVariableSeq_t* varSpec = (ListOfVariableSeq_t*) GLOBAL_CALLOC(1, sizeof(ListOfVariableSeq_t)); + + varSpec->variableSpecification.present = VariableSpecification_PR_name; + + if (domainId) { + varSpec->variableSpecification.choice.name.present = ObjectName_PR_domainspecific; + varSpec->variableSpecification.choice.name.choice.domainspecific.domainId.buf = (uint8_t*) domainId; + varSpec->variableSpecification.choice.name.choice.domainspecific.domainId.size = strlen(domainId); + varSpec->variableSpecification.choice.name.choice.domainspecific.itemId.buf = (uint8_t*) itemId; + varSpec->variableSpecification.choice.name.choice.domainspecific.itemId.size = strlen(itemId); + } + else if (associationSpecific) { + varSpec->variableSpecification.choice.name.present = ObjectName_PR_aaspecific; + varSpec->variableSpecification.choice.name.choice.aaspecific.buf = (uint8_t*) itemId; + varSpec->variableSpecification.choice.name.choice.aaspecific.size = strlen(itemId); + } + else { + varSpec->variableSpecification.choice.name.present = ObjectName_PR_vmdspecific; + varSpec->variableSpecification.choice.name.choice.vmdspecific.buf = (uint8_t*) itemId; + varSpec->variableSpecification.choice.name.choice.vmdspecific.size = strlen(itemId); + } + + if (componentName) + varSpec->alternateAccess = createAlternateAccessComponent(componentName); + + return varSpec; +} + +/** + * Request a single value with optional component + */ +int +mmsClient_createReadRequestComponent(uint32_t invokeId, const char* domainId, const char* itemId, const char* component, ByteBuffer* writeBuffer) +{ + MmsPdu_t* mmsPdu = mmsClient_createConfirmedRequestPdu(invokeId); + + ReadRequest_t* readRequest = createReadRequest(mmsPdu); + + readRequest->specificationWithResult = NULL; + + readRequest->variableAccessSpecification.present = VariableAccessSpecification_PR_listOfVariable; + readRequest->variableAccessSpecification.choice.listOfVariable.list.count = 1; + readRequest->variableAccessSpecification.choice.listOfVariable.list.size = 1; + readRequest->variableAccessSpecification.choice.listOfVariable.list.array = + (ListOfVariableSeq_t**) GLOBAL_CALLOC(1, sizeof(ListOfVariableSeq_t*)); + readRequest->variableAccessSpecification.choice.listOfVariable.list.array[0] = createNewVariableSpecification(domainId, itemId, component, false); + + asn_enc_rval_t rval; + + rval = der_encode(&asn_DEF_MmsPdu, mmsPdu, + (asn_app_consume_bytes_f*) mmsClient_write_out, (void*) writeBuffer); + + /* clean up data structures */ + deleteAlternateAccessComponent(readRequest->variableAccessSpecification.choice.listOfVariable.list.array[0]->alternateAccess); + + GLOBAL_FREEMEM(readRequest->variableAccessSpecification.choice.listOfVariable.list.array); + readRequest->variableAccessSpecification.choice.listOfVariable.list.array = NULL; + readRequest->variableAccessSpecification.choice.listOfVariable.list.count = 0; + asn_DEF_MmsPdu.free_struct(&asn_DEF_MmsPdu, mmsPdu, 0); + + return rval.encoded; +} + static AlternateAccess_t* -createAlternateAccessComponent(const char* componentName) +createAlternateAccess(uint32_t index, uint32_t elementCount) { AlternateAccess_t* alternateAccess = (AlternateAccess_t*) GLOBAL_CALLOC(1, sizeof(AlternateAccess_t)); alternateAccess->list.count = 1; @@ -578,32 +655,30 @@ createAlternateAccessComponent(const char* componentName) alternateAccess->list.array[0]->choice.unnamed = (AlternateAccessSelection_t*) GLOBAL_CALLOC(1, sizeof(AlternateAccessSelection_t)); - const char* separator = strchr(componentName, '$'); + alternateAccess->list.array[0]->choice.unnamed->present = AlternateAccessSelection_PR_selectAccess; - if (separator) { - int size = separator - componentName; + if (elementCount > 0) { + alternateAccess->list.array[0]->choice.unnamed->choice.selectAccess.present = + AlternateAccessSelection__selectAccess_PR_indexRange; - alternateAccess->list.array[0]->choice.unnamed->present = AlternateAccessSelection_PR_selectAlternateAccess; - alternateAccess->list.array[0]->choice.unnamed->choice.selectAlternateAccess.accessSelection.present = - AlternateAccessSelection__selectAlternateAccess__accessSelection_PR_component; + INTEGER_t* asnIndex = + &(alternateAccess->list.array[0]->choice.unnamed->choice.selectAccess.choice.indexRange.lowIndex); - alternateAccess->list.array[0]->choice.unnamed->choice.selectAlternateAccess.accessSelection.choice.component.buf = - (uint8_t*) StringUtils_copySubString((char*) componentName, (char*) separator); - alternateAccess->list.array[0]->choice.unnamed->choice.selectAlternateAccess.accessSelection.choice.component.size = size; + asn_long2INTEGER(asnIndex, index); - alternateAccess->list.array[0]->choice.unnamed->choice.selectAlternateAccess.alternateAccess = createAlternateAccessComponent(separator + 1); + asnIndex = + &(alternateAccess->list.array[0]->choice.unnamed->choice.selectAccess.choice.indexRange.numberOfElements); + + asn_long2INTEGER(asnIndex, elementCount); } else { - int size = strlen(componentName); - - alternateAccess->list.array[0]->choice.unnamed->present = AlternateAccessSelection_PR_selectAccess; - alternateAccess->list.array[0]->choice.unnamed->choice.selectAccess.present = - AlternateAccessSelection__selectAccess_PR_component; + AlternateAccessSelection__selectAccess_PR_index; - alternateAccess->list.array[0]->choice.unnamed->choice.selectAccess.choice.component.buf = - (uint8_t*) StringUtils_copyString(componentName); - alternateAccess->list.array[0]->choice.unnamed->choice.selectAccess.choice.component.size = size; + INTEGER_t* asnIndex = + &(alternateAccess->list.array[0]->choice.unnamed->choice.selectAccess.choice.index); + + asn_long2INTEGER(asnIndex, index); } return alternateAccess; diff --git a/src/mms/iso_mms/server/mms_read_service.c b/src/mms/iso_mms/server/mms_read_service.c index 5a7a62d2..caee6785 100644 --- a/src/mms/iso_mms/server/mms_read_service.c +++ b/src/mms/iso_mms/server/mms_read_service.c @@ -155,6 +155,51 @@ isAccessToArrayComponent(AlternateAccess_t* alternateAccess) return false; } +static MmsValue* +getComponent(AlternateAccess_t* alternateAccess, MmsVariableSpecification* namedVariable, MmsValue* variableValue) +{ + MmsValue* retValue = NULL; + + if (mmsServer_isComponentAccess(alternateAccess)) { + Identifier_t component = + alternateAccess->list.array[0]->choice.unnamed->choice.selectAccess.choice.component; + + if (component.size > 129) + goto exit_function; + + if (namedVariable->type == MMS_STRUCTURE) { + + int i; + + for (i = 0; i < namedVariable->typeSpec.structure.elementCount; + i++) { + + if (strlen(namedVariable->typeSpec.structure.elements[i]->name) + == component.size) { + if (strncmp( + namedVariable->typeSpec.structure.elements[i]->name, + (char*) component.buf, component.size) == 0) { + MmsValue* value = MmsValue_getElement(variableValue, i); + + if (alternateAccess->list.array[0]->choice.unnamed->choice.selectAlternateAccess.alternateAccess + != NULL) { + retValue = + getComponent( + alternateAccess->list.array[0]->choice.unnamed->choice.selectAlternateAccess.alternateAccess, + namedVariable->typeSpec.structure.elements[i], + value); + } + else + retValue = value; + } + } + } + } + } + + exit_function: return retValue; +} + static MmsValue* getComponentOfArrayElement(AlternateAccess_t* alternateAccess, MmsVariableSpecification* namedVariable, MmsValue* structuredValue) @@ -180,19 +225,26 @@ getComponentOfArrayElement(AlternateAccess_t* alternateAccess, MmsVariableSpecif int i; for (i = 0; i < structSpec->typeSpec.structure.elementCount; i++) { - if (strncmp (structSpec->typeSpec.structure.elements[i]->name, (char*) component.buf, - component.size) == 0) - { - MmsValue* value = MmsValue_getElement(structuredValue, i); - - if (isAccessToArrayComponent(alternateAccess->list.array[0]->choice.unnamed->choice.selectAlternateAccess.alternateAccess)) { - retValue = getComponentOfArrayElement(alternateAccess->list.array[0]->choice.unnamed->choice.selectAlternateAccess.alternateAccess, - structSpec->typeSpec.structure.elements[i], value); - } - else - retValue = value; - goto exit_function; + if (strlen(structSpec->typeSpec.structure.elements[i]->name) + == component.size) { + if (strncmp(structSpec->typeSpec.structure.elements[i]->name, + (char*) component.buf, component.size) == 0) { + MmsValue* value = MmsValue_getElement(structuredValue, i); + + if (isAccessToArrayComponent( + alternateAccess->list.array[0]->choice.unnamed->choice.selectAlternateAccess.alternateAccess)) { + retValue = + getComponentOfArrayElement( + alternateAccess->list.array[0]->choice.unnamed->choice.selectAlternateAccess.alternateAccess, + structSpec->typeSpec.structure.elements[i], + value); + } + else + retValue = value; + + goto exit_function; + } } } } @@ -288,12 +340,24 @@ addNamedVariableToResultList(MmsVariableSpecification* namedVariable, MmsDomain* MmsValue* value = mmsServer_getValue(connection->server, domain, nameIdStr, connection); - if (value != NULL) { - appendValueToResultList(value, values); + if (alternateAccess != NULL) { + value = getComponent(alternateAccess, namedVariable, value); + + if (value != NULL) { + appendValueToResultList(value, values); + } + else { + appendErrorToResultList(values, DATA_ACCESS_ERROR_OBJECT_NONE_EXISTENT); + } } else { - addComplexValueToResultList(namedVariable, - values, connection, domain, nameIdStr); + if (value != NULL) { + appendValueToResultList(value, values); + } + else { + addComplexValueToResultList(namedVariable, + values, connection, domain, nameIdStr); + } } } else if (namedVariable->type == MMS_ARRAY) { diff --git a/src/mms/iso_mms/server/mms_server_common.c b/src/mms/iso_mms/server/mms_server_common.c index e6522bf4..c2400a60 100644 --- a/src/mms/iso_mms/server/mms_server_common.c +++ b/src/mms/iso_mms/server/mms_server_common.c @@ -208,7 +208,7 @@ mmsMsg_createServiceErrorPdu(uint32_t invokeId, ByteBuffer* response, MmsError e mmsServer_createServiceErrorPduWithServiceSpecificInfo(invokeId, response, errorType, NULL, 0); } -int +bool mmsServer_isIndexAccess(AlternateAccess_t* alternateAccess) { if (alternateAccess->list.array[0]->present == AlternateAccess__Member_PR_unnamed) { @@ -217,13 +217,25 @@ mmsServer_isIndexAccess(AlternateAccess_t* alternateAccess) (alternateAccess->list.array[0]->choice.unnamed->choice.selectAccess.present == AlternateAccessSelection__selectAccess_PR_indexRange)) { - return 1; + return true; } - else - return 0; } - else - return 0; + + return false; +} + +bool +mmsServer_isComponentAccess(AlternateAccess_t* alternateAccess) +{ + if (alternateAccess->list.array[0]->present + == AlternateAccess__Member_PR_unnamed) { + if (alternateAccess->list.array[0]->choice.unnamed->choice.selectAccess.present + == AlternateAccessSelection__selectAccess_PR_component) { + return true; + } + } + + return false; } int