ua_services_attribute.c 50 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258
  1. /* This Source Code Form is subject to the terms of the Mozilla Public
  2. * License, v. 2.0. If a copy of the MPL was not distributed with this
  3. * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  4. #include "ua_server_internal.h"
  5. #include "ua_services.h"
  6. #ifdef UA_ENABLE_NONSTANDARD_STATELESS
  7. #include "ua_types_encoding_binary.h"
  8. #endif
  9. /* Force cast from const data for zero-copy reading. The storage type is set to
  10. nodelete. So the value is not deleted. Use with care! */
  11. static void
  12. forceVariantSetScalar(UA_Variant *v, const void *p, const UA_DataType *t) {
  13. UA_Variant_init(v);
  14. v->type = t;
  15. v->data = (void*)(uintptr_t)p;
  16. v->storageType = UA_VARIANT_DATA_NODELETE;
  17. }
  18. static UA_UInt32
  19. getUserWriteMask(UA_Server *server, const UA_Session *session, const UA_Node *node) {
  20. if(session == &adminSession)
  21. return 0xFFFFFFFF; /* the local admin user has all rights */
  22. return node->writeMask &
  23. server->config.accessControl.getUserRightsMask(&session->sessionId,
  24. session->sessionHandle,
  25. &node->nodeId);
  26. }
  27. static UA_Byte
  28. getUserAccessLevel(UA_Server *server, const UA_Session *session,
  29. const UA_VariableNode *node) {
  30. if(session == &adminSession)
  31. return 0xFF; /* the local admin user has all rights */
  32. return node->accessLevel &
  33. server->config.accessControl.getUserAccessLevel(&session->sessionId,
  34. session->sessionHandle,
  35. &node->nodeId);
  36. }
  37. static UA_Boolean
  38. getUserExecutable(UA_Server *server, const UA_Session *session,
  39. const UA_MethodNode *node) {
  40. if(session == &adminSession)
  41. return true; /* the local admin user has all rights */
  42. return node->executable &
  43. server->config.accessControl.getUserExecutable(&session->sessionId,
  44. session->sessionHandle,
  45. &node->nodeId);
  46. }
  47. /*****************/
  48. /* Type Checking */
  49. /*****************/
  50. enum type_equivalence {
  51. TYPE_EQUIVALENCE_NONE,
  52. TYPE_EQUIVALENCE_ENUM,
  53. TYPE_EQUIVALENCE_OPAQUE
  54. };
  55. static enum type_equivalence
  56. typeEquivalence(const UA_DataType *t) {
  57. if(t->membersSize != 1 || !t->members[0].namespaceZero)
  58. return TYPE_EQUIVALENCE_NONE;
  59. if(t->members[0].memberTypeIndex == UA_TYPES_INT32)
  60. return TYPE_EQUIVALENCE_ENUM;
  61. if(t->members[0].memberTypeIndex == UA_TYPES_BYTE && t->members[0].isArray)
  62. return TYPE_EQUIVALENCE_OPAQUE;
  63. return TYPE_EQUIVALENCE_NONE;
  64. }
  65. /* Test whether a valurank and the given arraydimensions are compatible. zero
  66. * array dimensions indicate a scalar */
  67. static UA_StatusCode
  68. compatibleValueRankArrayDimensions(UA_Int32 valueRank, size_t arrayDimensionsSize) {
  69. switch(valueRank) {
  70. case -3: /* the value can be a scalar or a one dimensional array */
  71. if(arrayDimensionsSize > 1)
  72. return UA_STATUSCODE_BADTYPEMISMATCH;
  73. break;
  74. case -2: /* the value can be a scalar or an array with any number of dimensions */
  75. break;
  76. case -1: /* the value is a scalar */
  77. if(arrayDimensionsSize > 0)
  78. return UA_STATUSCODE_BADTYPEMISMATCH;
  79. break;
  80. case 0: /* the value is an array with one or more dimensions */
  81. if(arrayDimensionsSize < 1)
  82. return UA_STATUSCODE_BADTYPEMISMATCH;
  83. break;
  84. default: /* >= 1: the value is an array with the specified number of dimensions */
  85. if(valueRank < 0)
  86. return UA_STATUSCODE_BADTYPEMISMATCH;
  87. /* Must hold if the array has a defined length. Null arrays (length -1)
  88. * need to be caught before. */
  89. if(arrayDimensionsSize != (size_t)valueRank)
  90. return UA_STATUSCODE_BADTYPEMISMATCH;
  91. }
  92. return UA_STATUSCODE_GOOD;
  93. }
  94. /* Check if the valuerank allows for the value dimension */
  95. static UA_StatusCode
  96. compatibleValueRankValue(UA_Int32 valueRank, const UA_Variant *value) {
  97. /* empty arrays (-1) always match */
  98. if(value->data == NULL)
  99. return UA_STATUSCODE_GOOD;
  100. size_t arrayDims = value->arrayDimensionsSize;
  101. if(!UA_Variant_isScalar(value))
  102. arrayDims = 1; /* array but no arraydimensions -> implicit array dimension 1 */
  103. return compatibleValueRankArrayDimensions(valueRank, arrayDims);
  104. }
  105. UA_StatusCode
  106. compatibleArrayDimensions(size_t constraintArrayDimensionsSize,
  107. const UA_UInt32 *constraintArrayDimensions,
  108. size_t testArrayDimensionsSize,
  109. const UA_UInt32 *testArrayDimensions) {
  110. if(testArrayDimensionsSize != constraintArrayDimensionsSize)
  111. return UA_STATUSCODE_BADTYPEMISMATCH;
  112. /* dimension size zero in the constraint is a wildcard */
  113. for(size_t i = 0; i < constraintArrayDimensionsSize; ++i) {
  114. if(constraintArrayDimensions[i] != testArrayDimensions[i] &&
  115. constraintArrayDimensions[i] != 0)
  116. return UA_STATUSCODE_BADTYPEMISMATCH;
  117. }
  118. return UA_STATUSCODE_GOOD;
  119. }
  120. /* Returns the pointer to a datavalue with a possibly transformed type to match
  121. the description */
  122. static const UA_Variant *
  123. convertToMatchingValue(UA_Server *server, const UA_Variant *value,
  124. const UA_NodeId *targetDataTypeId, UA_Variant *editableValue) {
  125. const UA_DataType *targetDataType = UA_findDataType(targetDataTypeId);
  126. if(!targetDataType)
  127. return NULL;
  128. /* A string is written to a byte array. the valuerank and array
  129. dimensions are checked later */
  130. if(targetDataType == &UA_TYPES[UA_TYPES_BYTE] &&
  131. value->type == &UA_TYPES[UA_TYPES_BYTESTRING] &&
  132. UA_Variant_isScalar(value)) {
  133. UA_ByteString *str = (UA_ByteString*)value->data;
  134. editableValue->storageType = UA_VARIANT_DATA_NODELETE;
  135. editableValue->type = &UA_TYPES[UA_TYPES_BYTE];
  136. editableValue->arrayLength = str->length;
  137. editableValue->data = str->data;
  138. return editableValue;
  139. }
  140. /* An enum was sent as an int32, or an opaque type as a bytestring. This
  141. * is detected with the typeIndex indicating the "true" datatype. */
  142. enum type_equivalence te1 = typeEquivalence(targetDataType);
  143. enum type_equivalence te2 = typeEquivalence(value->type);
  144. if(te1 != TYPE_EQUIVALENCE_NONE && te1 == te2) {
  145. *editableValue = *value;
  146. editableValue->storageType = UA_VARIANT_DATA_NODELETE;
  147. editableValue->type = targetDataType;
  148. return editableValue;
  149. }
  150. /* No more possible equivalencies */
  151. return NULL;
  152. }
  153. /* Test whether the value matches a variable definition given by
  154. * - datatype
  155. * - valueranke
  156. * - array dimensions.
  157. * Sometimes it can be necessary to transform the content of the value, e.g.
  158. * byte array to bytestring or uint32 to some enum. If editableValue is non-NULL,
  159. * we try to create a matching variant that points to the original data. */
  160. UA_StatusCode
  161. typeCheckValue(UA_Server *server, const UA_NodeId *targetDataTypeId,
  162. UA_Int32 targetValueRank, size_t targetArrayDimensionsSize,
  163. const UA_UInt32 *targetArrayDimensions, const UA_Variant *value,
  164. const UA_NumericRange *range, UA_Variant *editableValue) {
  165. const UA_NodeId subtypeId = UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE);
  166. /* Empty variant always matches... */
  167. if(!value->type)
  168. return UA_STATUSCODE_GOOD;
  169. /* See if the types match. The nodeid on the wire may be != the nodeid in
  170. * the node for opaque types, enums and bytestrings. value contains the
  171. * correct type definition after the following paragraph */
  172. if(UA_NodeId_equal(&value->type->typeId, targetDataTypeId))
  173. goto check_array;
  174. /* Has the value a subtype of the required type? */
  175. if(isNodeInTree(server->nodestore, &value->type->typeId, targetDataTypeId, &subtypeId, 1))
  176. goto check_array;
  177. /* Try to convert to a matching value if this is wanted */
  178. if(!editableValue)
  179. return UA_STATUSCODE_BADTYPEMISMATCH;
  180. value = convertToMatchingValue(server, value, targetDataTypeId, editableValue);
  181. if(!value)
  182. return UA_STATUSCODE_BADTYPEMISMATCH;
  183. check_array:
  184. if(range) /* array dimensions are checked later when writing the range */
  185. return UA_STATUSCODE_GOOD;
  186. size_t valueArrayDimensionsSize = value->arrayDimensionsSize;
  187. UA_UInt32 *valueArrayDimensions = value->arrayDimensions;
  188. UA_UInt32 tempArrayDimensions;
  189. if(valueArrayDimensions == 0 && !UA_Variant_isScalar(value)) {
  190. valueArrayDimensionsSize = 1;
  191. tempArrayDimensions = (UA_UInt32)value->arrayLength;
  192. valueArrayDimensions = &tempArrayDimensions;
  193. }
  194. /* See if the array dimensions match. When arrayDimensions are defined, they
  195. * already hold the valuerank. */
  196. if(targetArrayDimensionsSize > 0)
  197. return compatibleArrayDimensions(targetArrayDimensionsSize, targetArrayDimensions,
  198. valueArrayDimensionsSize, valueArrayDimensions);
  199. /* Check if the valuerank allows for the value dimension */
  200. return compatibleValueRankValue(targetValueRank, value);
  201. }
  202. /*****************************/
  203. /* ArrayDimensions Attribute */
  204. /*****************************/
  205. static UA_StatusCode
  206. readArrayDimensionsAttribute(const UA_VariableNode *vn, UA_DataValue *v) {
  207. UA_Variant_setArray(&v->value, vn->arrayDimensions,
  208. vn->arrayDimensionsSize, &UA_TYPES[UA_TYPES_INT32]);
  209. v->value.storageType = UA_VARIANT_DATA_NODELETE;
  210. v->hasValue = true;
  211. return UA_STATUSCODE_GOOD;
  212. }
  213. static UA_StatusCode
  214. writeArrayDimensionsAttribute(UA_Server *server, UA_VariableNode *node,
  215. size_t arrayDimensionsSize, UA_UInt32 *arrayDimensions) {
  216. /* If this is a variabletype, there must be no instances or subtypes of it
  217. * when we do the change */
  218. if(node->nodeClass == UA_NODECLASS_VARIABLETYPE &&
  219. UA_Node_hasSubTypeOrInstances((const UA_Node*)node))
  220. return UA_STATUSCODE_BADINTERNALERROR;
  221. /* Check that the array dimensions match with the valuerank */
  222. UA_StatusCode retval = compatibleValueRankArrayDimensions(node->valueRank, arrayDimensionsSize);
  223. if(retval != UA_STATUSCODE_GOOD) {
  224. UA_LOG_DEBUG(server->config.logger, UA_LOGCATEGORY_SERVER,
  225. "The current value rank does not match the new array dimensions");
  226. return retval;
  227. }
  228. /* Get the VariableType */
  229. const UA_VariableTypeNode *vt = getVariableNodeType(server, (UA_VariableNode*)node);
  230. if(!vt)
  231. return UA_STATUSCODE_BADINTERNALERROR;
  232. /* Check if the array dimensions match with the wildcards in the
  233. * variabletype (dimension length 0) */
  234. if(vt->arrayDimensions) {
  235. retval = compatibleArrayDimensions(vt->arrayDimensionsSize, vt->arrayDimensions,
  236. arrayDimensionsSize, arrayDimensions);
  237. if(retval != UA_STATUSCODE_GOOD) {
  238. UA_LOG_DEBUG(server->config.logger, UA_LOGCATEGORY_SERVER,
  239. "Array dimensions in the variable type do not match");
  240. return retval;
  241. }
  242. }
  243. /* Check if the current value is compatible with the array dimensions */
  244. UA_DataValue value;
  245. UA_DataValue_init(&value);
  246. retval = readValueAttribute(server, node, &value);
  247. if(retval != UA_STATUSCODE_GOOD)
  248. return retval;
  249. if(value.hasValue) {
  250. retval = compatibleArrayDimensions(arrayDimensionsSize, arrayDimensions,
  251. value.value.arrayDimensionsSize,
  252. value.value.arrayDimensions);
  253. UA_DataValue_deleteMembers(&value);
  254. if(retval != UA_STATUSCODE_GOOD) {
  255. UA_LOG_DEBUG(server->config.logger, UA_LOGCATEGORY_SERVER,
  256. "Array dimensions in the current value do not match");
  257. return retval;
  258. }
  259. }
  260. /* Ok, apply */
  261. UA_UInt32 *oldArrayDimensions = node->arrayDimensions;
  262. retval = UA_Array_copy(arrayDimensions, arrayDimensionsSize,
  263. (void**)&node->arrayDimensions, &UA_TYPES[UA_TYPES_UINT32]);
  264. if(retval != UA_STATUSCODE_GOOD)
  265. return retval;
  266. UA_free(oldArrayDimensions);
  267. node->arrayDimensionsSize = arrayDimensionsSize;
  268. return UA_STATUSCODE_GOOD;
  269. }
  270. /***********************/
  271. /* ValueRank Attribute */
  272. /***********************/
  273. static UA_StatusCode
  274. writeValueRankAttributeWithVT(UA_Server *server, UA_VariableNode *node,
  275. UA_Int32 valueRank) {
  276. const UA_VariableTypeNode *vt = getVariableNodeType(server, node);
  277. if(!vt)
  278. return UA_STATUSCODE_BADINTERNALERROR;
  279. return writeValueRankAttribute(server, node, valueRank, vt->valueRank);
  280. }
  281. UA_StatusCode
  282. writeValueRankAttribute(UA_Server *server, UA_VariableNode *node, UA_Int32 valueRank,
  283. UA_Int32 constraintValueRank) {
  284. /* If this is a variabletype, there must be no instances or subtypes of it
  285. when we do the change */
  286. if(node->nodeClass == UA_NODECLASS_VARIABLETYPE &&
  287. UA_Node_hasSubTypeOrInstances((const UA_Node*)node))
  288. return UA_STATUSCODE_BADINTERNALERROR;
  289. /* Check if the valuerank of the variabletype allows the change. */
  290. switch(constraintValueRank) {
  291. case -3: /* the value can be a scalar or a one dimensional array */
  292. if(valueRank != -1 && valueRank != 1)
  293. return UA_STATUSCODE_BADTYPEMISMATCH;
  294. break;
  295. case -2: /* the value can be a scalar or an array with any number of dimensions */
  296. break;
  297. case -1: /* the value is a scalar */
  298. if(valueRank != -1)
  299. return UA_STATUSCODE_BADTYPEMISMATCH;
  300. break;
  301. case 0: /* the value is an array with one or more dimensions */
  302. if(valueRank < 0)
  303. return UA_STATUSCODE_BADTYPEMISMATCH;
  304. break;
  305. default: /* >= 1: the value is an array with the specified number of dimensions */
  306. if(valueRank != constraintValueRank)
  307. return UA_STATUSCODE_BADTYPEMISMATCH;
  308. break;
  309. }
  310. /* Check if the new valuerank is compatible with the array dimensions. Use
  311. * the read service to handle data sources. */
  312. size_t arrayDims = node->arrayDimensionsSize;
  313. UA_StatusCode retval = UA_STATUSCODE_GOOD;
  314. if(arrayDims == 0) {
  315. /* the value could be an array with no arrayDimensions defined.
  316. dimensions zero indicate a scalar for compatibleValueRankArrayDimensions. */
  317. UA_DataValue value;
  318. UA_DataValue_init(&value);
  319. retval = readValueAttribute(server, node, &value);
  320. if(retval != UA_STATUSCODE_GOOD)
  321. return retval;
  322. if(!value.hasValue || value.value.data == NULL)
  323. goto apply; /* no value or null array */
  324. if(!UA_Variant_isScalar(&value.value))
  325. arrayDims = 1;
  326. UA_DataValue_deleteMembers(&value);
  327. }
  328. retval = compatibleValueRankArrayDimensions(valueRank, arrayDims);
  329. if(retval != UA_STATUSCODE_GOOD)
  330. return retval;
  331. /* All good, apply the change */
  332. apply:
  333. node->valueRank = valueRank;
  334. return UA_STATUSCODE_GOOD;
  335. }
  336. /**********************/
  337. /* DataType Attribute */
  338. /**********************/
  339. static UA_StatusCode
  340. writeDataTypeAttributeWithVT(UA_Server *server, UA_VariableNode *node,
  341. const UA_NodeId *dataType) {
  342. const UA_VariableTypeNode *vt = getVariableNodeType(server, node);
  343. if(!vt)
  344. return UA_STATUSCODE_BADINTERNALERROR;
  345. return writeDataTypeAttribute(server, node, dataType, &vt->dataType);
  346. }
  347. /* constraintDataType can be NULL, then we retrieve the vt */
  348. UA_StatusCode
  349. writeDataTypeAttribute(UA_Server *server, UA_VariableNode *node,
  350. const UA_NodeId *dataType, const UA_NodeId *constraintDataType) {
  351. /* If this is a variabletype, there must be no instances or subtypes of it
  352. when we do the change */
  353. if(node->nodeClass == UA_NODECLASS_VARIABLETYPE &&
  354. UA_Node_hasSubTypeOrInstances((const UA_Node*)node))
  355. return UA_STATUSCODE_BADINTERNALERROR;
  356. /* Does the new type match the constraints of the variabletype? */
  357. UA_NodeId subtypeId = UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE);
  358. if(!isNodeInTree(server->nodestore, dataType,
  359. constraintDataType, &subtypeId, 1))
  360. return UA_STATUSCODE_BADTYPEMISMATCH;
  361. /* Check if the current value would match the new type */
  362. UA_DataValue value;
  363. UA_DataValue_init(&value);
  364. UA_StatusCode retval = readValueAttribute(server, node, &value);
  365. if(retval != UA_STATUSCODE_GOOD)
  366. return retval;
  367. if(value.hasValue) {
  368. retval = typeCheckValue(server, dataType, node->valueRank,
  369. node->arrayDimensionsSize, node->arrayDimensions,
  370. &value.value, NULL, NULL);
  371. UA_DataValue_deleteMembers(&value);
  372. if(retval != UA_STATUSCODE_GOOD) {
  373. UA_LOG_DEBUG(server->config.logger, UA_LOGCATEGORY_SERVER,
  374. "The current value does not match the new data type");
  375. return retval;
  376. }
  377. }
  378. /* Replace the datatype nodeid */
  379. UA_NodeId dtCopy = node->dataType;
  380. retval = UA_NodeId_copy(dataType, &node->dataType);
  381. if(retval != UA_STATUSCODE_GOOD) {
  382. node->dataType = dtCopy;
  383. return retval;
  384. }
  385. UA_NodeId_deleteMembers(&dtCopy);
  386. return UA_STATUSCODE_GOOD;
  387. }
  388. /*******************/
  389. /* Value Attribute */
  390. /*******************/
  391. static UA_StatusCode
  392. readValueAttributeFromNode(UA_Server *server, const UA_VariableNode *vn, UA_DataValue *v,
  393. UA_NumericRange *rangeptr) {
  394. if(vn->value.data.callback.onRead) {
  395. UA_RCU_UNLOCK();
  396. vn->value.data.callback.onRead(vn->value.data.callback.handle,
  397. vn->nodeId, &vn->value.data.value.value, rangeptr);
  398. UA_RCU_LOCK();
  399. #ifdef UA_ENABLE_MULTITHREADING
  400. /* Reopen the node to see the changes (multithreading only) */
  401. vn = (const UA_VariableNode*)UA_NodeStore_get(server->nodestore, &vn->nodeId);
  402. #endif
  403. }
  404. if(rangeptr)
  405. return UA_Variant_copyRange(&vn->value.data.value.value, &v->value, *rangeptr);
  406. *v = vn->value.data.value;
  407. v->value.storageType = UA_VARIANT_DATA_NODELETE;
  408. return UA_STATUSCODE_GOOD;
  409. }
  410. static UA_StatusCode
  411. readValueAttributeFromDataSource(const UA_VariableNode *vn, UA_DataValue *v,
  412. UA_TimestampsToReturn timestamps,
  413. UA_NumericRange *rangeptr) {
  414. if(!vn->value.dataSource.read)
  415. return UA_STATUSCODE_BADINTERNALERROR;
  416. UA_Boolean sourceTimeStamp = (timestamps == UA_TIMESTAMPSTORETURN_SOURCE ||
  417. timestamps == UA_TIMESTAMPSTORETURN_BOTH);
  418. UA_RCU_UNLOCK();
  419. UA_StatusCode retval =
  420. vn->value.dataSource.read(vn->value.dataSource.handle, vn->nodeId,
  421. sourceTimeStamp, rangeptr, v);
  422. UA_RCU_LOCK();
  423. return retval;
  424. }
  425. static UA_StatusCode
  426. readValueAttributeComplete(UA_Server *server, const UA_VariableNode *vn,
  427. UA_TimestampsToReturn timestamps, const UA_String *indexRange,
  428. UA_DataValue *v) {
  429. /* Compute the index range */
  430. UA_NumericRange range;
  431. UA_NumericRange *rangeptr = NULL;
  432. UA_StatusCode retval = UA_STATUSCODE_GOOD;
  433. if(indexRange && indexRange->length > 0) {
  434. retval = parse_numericrange(indexRange, &range);
  435. if(retval != UA_STATUSCODE_GOOD)
  436. return retval;
  437. rangeptr = &range;
  438. }
  439. /* Read the value */
  440. if(vn->valueSource == UA_VALUESOURCE_DATA)
  441. retval = readValueAttributeFromNode(server, vn, v, rangeptr);
  442. else
  443. retval = readValueAttributeFromDataSource(vn, v, timestamps, rangeptr);
  444. /* Clean up */
  445. if(rangeptr)
  446. UA_free(range.dimensions);
  447. return retval;
  448. }
  449. UA_StatusCode
  450. readValueAttribute(UA_Server *server, const UA_VariableNode *vn, UA_DataValue *v) {
  451. return readValueAttributeComplete(server, vn, UA_TIMESTAMPSTORETURN_NEITHER, NULL, v);
  452. }
  453. static UA_StatusCode
  454. writeValueAttributeWithoutRange(UA_VariableNode *node, const UA_DataValue *value) {
  455. UA_DataValue old_value = node->value.data.value; /* keep the pointers for restoring */
  456. UA_StatusCode retval = UA_DataValue_copy(value, &node->value.data.value);
  457. if(retval == UA_STATUSCODE_GOOD)
  458. UA_DataValue_deleteMembers(&old_value);
  459. else
  460. node->value.data.value = old_value;
  461. return retval;
  462. }
  463. static UA_StatusCode
  464. writeValueAttributeWithRange(UA_VariableNode *node, const UA_DataValue *value,
  465. const UA_NumericRange *rangeptr) {
  466. /* Value on both sides? */
  467. if(value->status != node->value.data.value.status ||
  468. !value->hasValue || !node->value.data.value.hasValue)
  469. return UA_STATUSCODE_BADINDEXRANGEINVALID;
  470. /* Make scalar a one-entry array for range matching */
  471. UA_Variant editableValue;
  472. const UA_Variant *v = &value->value;
  473. if(UA_Variant_isScalar(&value->value)) {
  474. editableValue = value->value;
  475. editableValue.arrayLength = 1;
  476. v = &editableValue;
  477. }
  478. /* Write the value */
  479. UA_StatusCode retval = UA_Variant_setRangeCopy(&node->value.data.value.value,
  480. v->data, v->arrayLength, *rangeptr);
  481. if(retval != UA_STATUSCODE_GOOD)
  482. return retval;
  483. /* Write the status and timestamps */
  484. node->value.data.value.hasStatus = value->hasStatus;
  485. node->value.data.value.status = value->status;
  486. node->value.data.value.hasSourceTimestamp = value->hasSourceTimestamp;
  487. node->value.data.value.sourceTimestamp = value->sourceTimestamp;
  488. node->value.data.value.hasSourcePicoseconds = value->hasSourcePicoseconds;
  489. node->value.data.value.sourcePicoseconds = value->sourcePicoseconds;
  490. return UA_STATUSCODE_GOOD;
  491. }
  492. UA_StatusCode
  493. writeValueAttribute(UA_Server *server, UA_VariableNode *node,
  494. const UA_DataValue *value, const UA_String *indexRange) {
  495. /* Parse the range */
  496. UA_NumericRange range;
  497. UA_NumericRange *rangeptr = NULL;
  498. UA_StatusCode retval = UA_STATUSCODE_GOOD;
  499. if(indexRange && indexRange->length > 0) {
  500. retval = parse_numericrange(indexRange, &range);
  501. if(retval != UA_STATUSCODE_GOOD)
  502. return retval;
  503. rangeptr = &range;
  504. }
  505. /* Copy the value into an editable "container" where e.g. the datatype can
  506. * be adjusted. The data itself is not written into. */
  507. UA_DataValue editableValue = *value;
  508. editableValue.value.storageType = UA_VARIANT_DATA_NODELETE;
  509. /* Type checking. May change the type of editableValue */
  510. if(value->hasValue) {
  511. retval = typeCheckValue(server, &node->dataType, node->valueRank,
  512. node->arrayDimensionsSize, node->arrayDimensions,
  513. &value->value, rangeptr, &editableValue.value);
  514. if(retval != UA_STATUSCODE_GOOD)
  515. goto cleanup;
  516. }
  517. /* Set the source timestamp if there is none */
  518. if(!editableValue.hasSourceTimestamp) {
  519. editableValue.sourceTimestamp = UA_DateTime_now();
  520. editableValue.hasSourceTimestamp = true;
  521. }
  522. /* Ok, do it */
  523. if(node->valueSource == UA_VALUESOURCE_DATA) {
  524. if(!rangeptr)
  525. retval = writeValueAttributeWithoutRange(node, &editableValue);
  526. else
  527. retval = writeValueAttributeWithRange(node, &editableValue, rangeptr);
  528. /* Callback after writing */
  529. if(retval == UA_STATUSCODE_GOOD && node->value.data.callback.onWrite) {
  530. const UA_VariableNode *writtenNode;
  531. #ifdef UA_ENABLE_MULTITHREADING
  532. /* Reopen the node to see the changes (multithreading only) */
  533. writtenNode = (const UA_VariableNode*)UA_NodeStore_get(server->nodestore, &node->nodeId);
  534. #else
  535. writtenNode = node; /* The node is written in-situ (TODO: this might
  536. change with the nodestore plugin approach) */
  537. #endif
  538. UA_RCU_UNLOCK();
  539. writtenNode->value.data.callback.onWrite(writtenNode->value.data.callback.handle,
  540. writtenNode->nodeId,
  541. &writtenNode->value.data.value.value, rangeptr);
  542. UA_RCU_LOCK();
  543. }
  544. } else {
  545. if(node->value.dataSource.write) {
  546. UA_RCU_UNLOCK();
  547. retval = node->value.dataSource.write(node->value.dataSource.handle,
  548. node->nodeId, &editableValue.value, rangeptr);
  549. UA_RCU_LOCK();
  550. } else {
  551. retval = UA_STATUSCODE_BADWRITENOTSUPPORTED;
  552. }
  553. }
  554. /* Clean up */
  555. cleanup:
  556. if(rangeptr)
  557. UA_free(range.dimensions);
  558. return retval;
  559. }
  560. /************************/
  561. /* IsAbstract Attribute */
  562. /************************/
  563. static UA_StatusCode
  564. readIsAbstractAttribute(const UA_Node *node, UA_Variant *v) {
  565. const UA_Boolean *isAbstract;
  566. switch(node->nodeClass) {
  567. case UA_NODECLASS_REFERENCETYPE:
  568. isAbstract = &((const UA_ReferenceTypeNode*)node)->isAbstract;
  569. break;
  570. case UA_NODECLASS_OBJECTTYPE:
  571. isAbstract = &((const UA_ObjectTypeNode*)node)->isAbstract;
  572. break;
  573. case UA_NODECLASS_VARIABLETYPE:
  574. isAbstract = &((const UA_VariableTypeNode*)node)->isAbstract;
  575. break;
  576. case UA_NODECLASS_DATATYPE:
  577. isAbstract = &((const UA_DataTypeNode*)node)->isAbstract;
  578. break;
  579. default:
  580. return UA_STATUSCODE_BADATTRIBUTEIDINVALID;
  581. }
  582. forceVariantSetScalar(v, isAbstract, &UA_TYPES[UA_TYPES_BOOLEAN]);
  583. return UA_STATUSCODE_GOOD;
  584. }
  585. static UA_StatusCode
  586. writeIsAbstractAttribute(UA_Node *node, UA_Boolean value) {
  587. switch(node->nodeClass) {
  588. case UA_NODECLASS_OBJECTTYPE:
  589. ((UA_ObjectTypeNode*)node)->isAbstract = value;
  590. break;
  591. case UA_NODECLASS_REFERENCETYPE:
  592. ((UA_ReferenceTypeNode*)node)->isAbstract = value;
  593. break;
  594. case UA_NODECLASS_VARIABLETYPE:
  595. ((UA_VariableTypeNode*)node)->isAbstract = value;
  596. break;
  597. case UA_NODECLASS_DATATYPE:
  598. ((UA_DataTypeNode*)node)->isAbstract = value;
  599. break;
  600. default:
  601. return UA_STATUSCODE_BADNODECLASSINVALID;
  602. }
  603. return UA_STATUSCODE_GOOD;
  604. }
  605. /****************/
  606. /* Read Service */
  607. /****************/
  608. static const UA_String binEncoding = {sizeof("DefaultBinary")-1, (UA_Byte*)"DefaultBinary"};
  609. /* static const UA_String xmlEncoding = {sizeof("DefaultXml")-1, (UA_Byte*)"DefaultXml"}; */
  610. #define CHECK_NODECLASS(CLASS) \
  611. if(!(node->nodeClass & (CLASS))) { \
  612. retval = UA_STATUSCODE_BADATTRIBUTEIDINVALID; \
  613. break; \
  614. }
  615. void Service_Read_single(UA_Server *server, UA_Session *session,
  616. const UA_TimestampsToReturn timestamps,
  617. const UA_ReadValueId *id, UA_DataValue *v) {
  618. UA_LOG_DEBUG_SESSION(server->config.logger, session,
  619. "Read the attribute %i", id->attributeId);
  620. /* XML encoding is not supported */
  621. if(id->dataEncoding.name.length > 0 &&
  622. !UA_String_equal(&binEncoding, &id->dataEncoding.name)) {
  623. v->hasStatus = true;
  624. v->status = UA_STATUSCODE_BADDATAENCODINGUNSUPPORTED;
  625. return;
  626. }
  627. /* Index range for an attribute other than value */
  628. if(id->indexRange.length > 0 && id->attributeId != UA_ATTRIBUTEID_VALUE) {
  629. v->hasStatus = true;
  630. v->status = UA_STATUSCODE_BADINDEXRANGENODATA;
  631. return;
  632. }
  633. /* Get the node */
  634. const UA_Node *node = UA_NodeStore_get(server->nodestore, &id->nodeId);
  635. if(!node) {
  636. v->hasStatus = true;
  637. v->status = UA_STATUSCODE_BADNODEIDUNKNOWN;
  638. return;
  639. }
  640. /* Read the attribute */
  641. UA_StatusCode retval = UA_STATUSCODE_GOOD;
  642. switch(id->attributeId) {
  643. case UA_ATTRIBUTEID_NODEID:
  644. forceVariantSetScalar(&v->value, &node->nodeId, &UA_TYPES[UA_TYPES_NODEID]);
  645. break;
  646. case UA_ATTRIBUTEID_NODECLASS:
  647. forceVariantSetScalar(&v->value, &node->nodeClass, &UA_TYPES[UA_TYPES_NODECLASS]);
  648. break;
  649. case UA_ATTRIBUTEID_BROWSENAME:
  650. forceVariantSetScalar(&v->value, &node->browseName, &UA_TYPES[UA_TYPES_QUALIFIEDNAME]);
  651. break;
  652. case UA_ATTRIBUTEID_DISPLAYNAME:
  653. forceVariantSetScalar(&v->value, &node->displayName, &UA_TYPES[UA_TYPES_LOCALIZEDTEXT]);
  654. break;
  655. case UA_ATTRIBUTEID_DESCRIPTION:
  656. forceVariantSetScalar(&v->value, &node->description, &UA_TYPES[UA_TYPES_LOCALIZEDTEXT]);
  657. break;
  658. case UA_ATTRIBUTEID_WRITEMASK:
  659. forceVariantSetScalar(&v->value, &node->writeMask, &UA_TYPES[UA_TYPES_UINT32]);
  660. break;
  661. case UA_ATTRIBUTEID_USERWRITEMASK: {
  662. UA_UInt32 userWriteMask = getUserWriteMask(server, session, node);
  663. UA_Variant_setScalarCopy(&v->value, &userWriteMask, &UA_TYPES[UA_TYPES_UINT32]);
  664. break; }
  665. case UA_ATTRIBUTEID_ISABSTRACT:
  666. retval = readIsAbstractAttribute(node, &v->value);
  667. break;
  668. case UA_ATTRIBUTEID_SYMMETRIC:
  669. CHECK_NODECLASS(UA_NODECLASS_REFERENCETYPE);
  670. forceVariantSetScalar(&v->value, &((const UA_ReferenceTypeNode*)node)->symmetric,
  671. &UA_TYPES[UA_TYPES_BOOLEAN]);
  672. break;
  673. case UA_ATTRIBUTEID_INVERSENAME:
  674. CHECK_NODECLASS(UA_NODECLASS_REFERENCETYPE);
  675. forceVariantSetScalar(&v->value, &((const UA_ReferenceTypeNode*)node)->inverseName,
  676. &UA_TYPES[UA_TYPES_LOCALIZEDTEXT]);
  677. break;
  678. case UA_ATTRIBUTEID_CONTAINSNOLOOPS:
  679. CHECK_NODECLASS(UA_NODECLASS_VIEW);
  680. forceVariantSetScalar(&v->value, &((const UA_ViewNode*)node)->containsNoLoops,
  681. &UA_TYPES[UA_TYPES_BOOLEAN]);
  682. break;
  683. case UA_ATTRIBUTEID_EVENTNOTIFIER:
  684. CHECK_NODECLASS(UA_NODECLASS_VIEW | UA_NODECLASS_OBJECT);
  685. forceVariantSetScalar(&v->value, &((const UA_ViewNode*)node)->eventNotifier,
  686. &UA_TYPES[UA_TYPES_BYTE]);
  687. break;
  688. case UA_ATTRIBUTEID_VALUE:
  689. CHECK_NODECLASS(UA_NODECLASS_VARIABLE | UA_NODECLASS_VARIABLETYPE);
  690. retval = readValueAttributeComplete(server, (const UA_VariableNode*)node,
  691. timestamps, &id->indexRange, v);
  692. break;
  693. case UA_ATTRIBUTEID_DATATYPE:
  694. CHECK_NODECLASS(UA_NODECLASS_VARIABLE | UA_NODECLASS_VARIABLETYPE);
  695. forceVariantSetScalar(&v->value, &((const UA_VariableTypeNode*)node)->dataType,
  696. &UA_TYPES[UA_TYPES_NODEID]);
  697. break;
  698. case UA_ATTRIBUTEID_VALUERANK:
  699. CHECK_NODECLASS(UA_NODECLASS_VARIABLE | UA_NODECLASS_VARIABLETYPE);
  700. forceVariantSetScalar(&v->value, &((const UA_VariableTypeNode*)node)->valueRank,
  701. &UA_TYPES[UA_TYPES_INT32]);
  702. break;
  703. case UA_ATTRIBUTEID_ARRAYDIMENSIONS:
  704. CHECK_NODECLASS(UA_NODECLASS_VARIABLE | UA_NODECLASS_VARIABLETYPE);
  705. retval = readArrayDimensionsAttribute((const UA_VariableNode*)node, v);
  706. break;
  707. case UA_ATTRIBUTEID_ACCESSLEVEL:
  708. CHECK_NODECLASS(UA_NODECLASS_VARIABLE);
  709. forceVariantSetScalar(&v->value, &((const UA_VariableNode*)node)->accessLevel,
  710. &UA_TYPES[UA_TYPES_BYTE]);
  711. break;
  712. case UA_ATTRIBUTEID_USERACCESSLEVEL: {
  713. CHECK_NODECLASS(UA_NODECLASS_VARIABLE);
  714. UA_Byte userAccessLevel = getUserAccessLevel(server, session, (const UA_VariableNode*)node);
  715. UA_Variant_setScalarCopy(&v->value, &userAccessLevel, &UA_TYPES[UA_TYPES_BYTE]);
  716. break; }
  717. case UA_ATTRIBUTEID_MINIMUMSAMPLINGINTERVAL:
  718. CHECK_NODECLASS(UA_NODECLASS_VARIABLE);
  719. forceVariantSetScalar(&v->value, &((const UA_VariableNode*)node)->minimumSamplingInterval,
  720. &UA_TYPES[UA_TYPES_DOUBLE]);
  721. break;
  722. case UA_ATTRIBUTEID_HISTORIZING:
  723. CHECK_NODECLASS(UA_NODECLASS_VARIABLE);
  724. forceVariantSetScalar(&v->value, &((const UA_VariableNode*)node)->historizing,
  725. &UA_TYPES[UA_TYPES_BOOLEAN]);
  726. break;
  727. case UA_ATTRIBUTEID_EXECUTABLE:
  728. CHECK_NODECLASS(UA_NODECLASS_METHOD);
  729. forceVariantSetScalar(&v->value, &((const UA_MethodNode*)node)->executable,
  730. &UA_TYPES[UA_TYPES_BOOLEAN]);
  731. break;
  732. case UA_ATTRIBUTEID_USEREXECUTABLE: {
  733. CHECK_NODECLASS(UA_NODECLASS_METHOD);
  734. UA_Boolean userExecutable = getUserExecutable(server, session, (const UA_MethodNode*)node);
  735. UA_Variant_setScalarCopy(&v->value, &userExecutable, &UA_TYPES[UA_TYPES_BOOLEAN]);
  736. break; }
  737. default:
  738. retval = UA_STATUSCODE_BADATTRIBUTEIDINVALID;
  739. }
  740. /* Return error code when reading has failed */
  741. if(retval != UA_STATUSCODE_GOOD) {
  742. v->hasStatus = true;
  743. v->status = retval;
  744. return;
  745. }
  746. v->hasValue = true;
  747. /* Create server timestamp */
  748. if(timestamps == UA_TIMESTAMPSTORETURN_SERVER ||
  749. timestamps == UA_TIMESTAMPSTORETURN_BOTH) {
  750. v->serverTimestamp = UA_DateTime_now();
  751. v->hasServerTimestamp = true;
  752. }
  753. /* Handle source time stamp */
  754. if(id->attributeId == UA_ATTRIBUTEID_VALUE) {
  755. if (timestamps == UA_TIMESTAMPSTORETURN_SERVER ||
  756. timestamps == UA_TIMESTAMPSTORETURN_NEITHER) {
  757. v->hasSourceTimestamp = false;
  758. v->hasSourcePicoseconds = false;
  759. } else if(!v->hasSourceTimestamp) {
  760. v->sourceTimestamp = UA_DateTime_now();
  761. v->hasSourceTimestamp = true;
  762. }
  763. }
  764. }
  765. void Service_Read(UA_Server *server, UA_Session *session,
  766. const UA_ReadRequest *request, UA_ReadResponse *response) {
  767. UA_LOG_DEBUG_SESSION(server->config.logger, session, "Processing ReadRequest");
  768. if(request->nodesToReadSize <= 0) {
  769. response->responseHeader.serviceResult = UA_STATUSCODE_BADNOTHINGTODO;
  770. return;
  771. }
  772. /* check if the timestampstoreturn is valid */
  773. if(request->timestampsToReturn > UA_TIMESTAMPSTORETURN_NEITHER) {
  774. response->responseHeader.serviceResult = UA_STATUSCODE_BADTIMESTAMPSTORETURNINVALID;
  775. return;
  776. }
  777. size_t size = request->nodesToReadSize;
  778. response->results = (UA_DataValue *)UA_Array_new(size, &UA_TYPES[UA_TYPES_DATAVALUE]);
  779. if(!response->results) {
  780. response->responseHeader.serviceResult = UA_STATUSCODE_BADOUTOFMEMORY;
  781. return;
  782. }
  783. response->resultsSize = size;
  784. if(request->maxAge < 0) {
  785. response->responseHeader.serviceResult = UA_STATUSCODE_BADMAXAGEINVALID;
  786. return;
  787. }
  788. #ifdef UA_ENABLE_EXTERNAL_NAMESPACES
  789. UA_Boolean isExternal[size];
  790. UA_UInt32 indices[size];
  791. memset(isExternal, false, sizeof(UA_Boolean) * size);
  792. for(size_t j = 0;j<server->externalNamespacesSize;++j) {
  793. size_t indexSize = 0;
  794. for(size_t i = 0;i < size;++i) {
  795. if(request->nodesToRead[i].nodeId.namespaceIndex != server->externalNamespaces[j].index)
  796. continue;
  797. isExternal[i] = true;
  798. indices[indexSize] = (UA_UInt32)i;
  799. ++indexSize;
  800. }
  801. if(indexSize == 0)
  802. continue;
  803. UA_ExternalNodeStore *ens = &server->externalNamespaces[j].externalNodeStore;
  804. ens->readNodes(ens->ensHandle, &request->requestHeader, request->nodesToRead,
  805. indices, (UA_UInt32)indexSize, response->results, false,
  806. response->diagnosticInfos);
  807. }
  808. #endif
  809. for(size_t i = 0;i < size;++i) {
  810. #ifdef UA_ENABLE_EXTERNAL_NAMESPACES
  811. if(!isExternal[i])
  812. #endif
  813. Service_Read_single(server, session, request->timestampsToReturn,
  814. &request->nodesToRead[i], &response->results[i]);
  815. }
  816. #ifdef UA_ENABLE_NONSTANDARD_STATELESS
  817. /* Add an expiry header for caching */
  818. if(session->sessionId.namespaceIndex == 0 &&
  819. session->sessionId.identifierType == UA_NODEIDTYPE_NUMERIC &&
  820. session->sessionId.identifier.numeric == 0){
  821. UA_ExtensionObject additionalHeader;
  822. UA_ExtensionObject_init(&additionalHeader);
  823. additionalHeader.encoding = UA_EXTENSIONOBJECT_ENCODED_BYTESTRING;
  824. additionalHeader.content.encoded.typeId =UA_TYPES[UA_TYPES_VARIANT].typeId;
  825. UA_Variant variant;
  826. UA_Variant_init(&variant);
  827. UA_DateTime* expireArray = NULL;
  828. expireArray = UA_Array_new(request->nodesToReadSize,
  829. &UA_TYPES[UA_TYPES_DATETIME]);
  830. variant.data = expireArray;
  831. /* expires in 20 seconds */
  832. for(UA_UInt32 i = 0;i < response->resultsSize;++i) {
  833. expireArray[i] = UA_DateTime_now() + 20 * 100 * 1000 * 1000;
  834. }
  835. UA_Variant_setArray(&variant, expireArray, request->nodesToReadSize,
  836. &UA_TYPES[UA_TYPES_DATETIME]);
  837. size_t offset = 0;
  838. UA_ByteString str;
  839. size_t strlength = UA_calcSizeBinary(&variant, &UA_TYPES[UA_TYPES_VARIANT]);
  840. UA_ByteString_allocBuffer(&str, strlength);
  841. /* No chunking callback for the encoding */
  842. UA_StatusCode retval = UA_encodeBinary(&variant, &UA_TYPES[UA_TYPES_VARIANT],
  843. NULL, NULL, &str, &offset);
  844. UA_Array_delete(expireArray, request->nodesToReadSize, &UA_TYPES[UA_TYPES_DATETIME]);
  845. if(retval == UA_STATUSCODE_GOOD){
  846. additionalHeader.content.encoded.body.data = str.data;
  847. additionalHeader.content.encoded.body.length = offset;
  848. response->responseHeader.additionalHeader = additionalHeader;
  849. }
  850. }
  851. #endif
  852. }
  853. /* Exposes the Read service to local users */
  854. UA_DataValue
  855. UA_Server_read(UA_Server *server, const UA_ReadValueId *item,
  856. UA_TimestampsToReturn timestamps) {
  857. UA_DataValue dv;
  858. UA_DataValue_init(&dv);
  859. UA_RCU_LOCK();
  860. Service_Read_single(server, &adminSession, timestamps, item, &dv);
  861. UA_RCU_UNLOCK();
  862. return dv;
  863. }
  864. /* Used in inline functions exposing the Read service with more syntactic sugar
  865. * for individual attributes */
  866. UA_StatusCode
  867. __UA_Server_read(UA_Server *server, const UA_NodeId *nodeId,
  868. const UA_AttributeId attributeId, void *v) {
  869. /* Call the read service */
  870. UA_ReadValueId item;
  871. UA_ReadValueId_init(&item);
  872. item.nodeId = *nodeId;
  873. item.attributeId = attributeId;
  874. UA_DataValue dv = UA_Server_read(server, &item, UA_TIMESTAMPSTORETURN_NEITHER);
  875. /* Check the return value */
  876. UA_StatusCode retval = UA_STATUSCODE_GOOD;
  877. if(dv.hasStatus)
  878. retval = dv.hasStatus;
  879. else if(!dv.hasValue)
  880. retval = UA_STATUSCODE_BADUNEXPECTEDERROR;
  881. if(retval != UA_STATUSCODE_GOOD) {
  882. UA_DataValue_deleteMembers(&dv);
  883. return retval;
  884. }
  885. /* Prepare the result */
  886. if(attributeId == UA_ATTRIBUTEID_VALUE ||
  887. attributeId == UA_ATTRIBUTEID_ARRAYDIMENSIONS) {
  888. /* Return the entire variant */
  889. if(dv.value.storageType == UA_VARIANT_DATA_NODELETE) {
  890. retval = UA_Variant_copy(&dv.value,(UA_Variant *) v);
  891. } else {
  892. /* storageType is UA_VARIANT_DATA. Copy the entire variant
  893. * (including pointers and all) */
  894. memcpy(v, &dv.value, sizeof(UA_Variant));
  895. }
  896. } else {
  897. /* Return the variant content only */
  898. if(dv.value.storageType == UA_VARIANT_DATA_NODELETE) {
  899. retval = UA_copy(dv.value.data, v, dv.value.type);
  900. } else {
  901. /* storageType is UA_VARIANT_DATA. Copy the content of the type
  902. * (including pointers and all) */
  903. memcpy(v, dv.value.data, dv.value.type->memSize);
  904. /* Delete the "carrier" in the variant */
  905. UA_free(dv.value.data);
  906. }
  907. }
  908. return retval;
  909. }
  910. /*****************/
  911. /* Write Service */
  912. /*****************/
  913. #define CHECK_DATATYPE_SCALAR(EXP_DT) \
  914. if(!wvalue->value.hasValue || \
  915. &UA_TYPES[UA_TYPES_##EXP_DT] != wvalue->value.value.type || \
  916. !UA_Variant_isScalar(&wvalue->value.value)) { \
  917. retval = UA_STATUSCODE_BADTYPEMISMATCH; \
  918. break; \
  919. }
  920. #define CHECK_DATATYPE_ARRAY(EXP_DT) \
  921. if(!wvalue->value.hasValue || \
  922. &UA_TYPES[UA_TYPES_##EXP_DT] != wvalue->value.value.type || \
  923. UA_Variant_isScalar(&wvalue->value.value)) { \
  924. retval = UA_STATUSCODE_BADTYPEMISMATCH; \
  925. break; \
  926. }
  927. #define CHECK_NODECLASS_WRITE(CLASS) \
  928. if((node->nodeClass & (CLASS)) == 0) { \
  929. retval = UA_STATUSCODE_BADNODECLASSINVALID; \
  930. break; \
  931. }
  932. #define CHECK_USERWRITEMASK(BITPOS) \
  933. if(!(userWriteMask & (((UA_UInt32)0x01) << BITPOS ))) { \
  934. retval = UA_STATUSCODE_BADUSERACCESSDENIED; \
  935. break; \
  936. }
  937. /* This function implements the main part of the write service and operates on a
  938. copy of the node (not in single-threaded mode). */
  939. static UA_StatusCode
  940. CopyAttributeIntoNode(UA_Server *server, UA_Session *session,
  941. UA_Node *node, const UA_WriteValue *wvalue) {
  942. const void *value = wvalue->value.value.data;
  943. UA_UInt32 userWriteMask = getUserWriteMask(server, session, node);
  944. UA_StatusCode retval = UA_STATUSCODE_GOOD;
  945. switch(wvalue->attributeId) {
  946. case UA_ATTRIBUTEID_NODEID:
  947. case UA_ATTRIBUTEID_NODECLASS:
  948. case UA_ATTRIBUTEID_USERWRITEMASK:
  949. case UA_ATTRIBUTEID_USERACCESSLEVEL:
  950. case UA_ATTRIBUTEID_USEREXECUTABLE:
  951. retval = UA_STATUSCODE_BADWRITENOTSUPPORTED;
  952. break;
  953. case UA_ATTRIBUTEID_BROWSENAME:
  954. CHECK_USERWRITEMASK(2);
  955. CHECK_DATATYPE_SCALAR(QUALIFIEDNAME);
  956. UA_QualifiedName_deleteMembers(&node->browseName);
  957. UA_QualifiedName_copy((const UA_QualifiedName *)value, &node->browseName);
  958. break;
  959. case UA_ATTRIBUTEID_DISPLAYNAME:
  960. CHECK_USERWRITEMASK(6);
  961. CHECK_DATATYPE_SCALAR(LOCALIZEDTEXT);
  962. UA_LocalizedText_deleteMembers(&node->displayName);
  963. UA_LocalizedText_copy((const UA_LocalizedText *)value, &node->displayName);
  964. break;
  965. case UA_ATTRIBUTEID_DESCRIPTION:
  966. CHECK_USERWRITEMASK(5);
  967. CHECK_DATATYPE_SCALAR(LOCALIZEDTEXT);
  968. UA_LocalizedText_deleteMembers(&node->description);
  969. UA_LocalizedText_copy((const UA_LocalizedText *)value, &node->description);
  970. break;
  971. case UA_ATTRIBUTEID_WRITEMASK:
  972. CHECK_USERWRITEMASK(20);
  973. CHECK_DATATYPE_SCALAR(UINT32);
  974. node->writeMask = *(const UA_UInt32*)value;
  975. break;
  976. case UA_ATTRIBUTEID_ISABSTRACT:
  977. CHECK_USERWRITEMASK(11);
  978. CHECK_DATATYPE_SCALAR(BOOLEAN);
  979. retval = writeIsAbstractAttribute(node, *(const UA_Boolean*)value);
  980. break;
  981. case UA_ATTRIBUTEID_SYMMETRIC:
  982. CHECK_NODECLASS_WRITE(UA_NODECLASS_REFERENCETYPE);
  983. CHECK_USERWRITEMASK(15);
  984. CHECK_DATATYPE_SCALAR(BOOLEAN);
  985. ((UA_ReferenceTypeNode*)node)->symmetric = *(const UA_Boolean*)value;
  986. break;
  987. case UA_ATTRIBUTEID_INVERSENAME:
  988. CHECK_NODECLASS_WRITE(UA_NODECLASS_REFERENCETYPE);
  989. CHECK_USERWRITEMASK(10);
  990. CHECK_DATATYPE_SCALAR(LOCALIZEDTEXT);
  991. UA_LocalizedText_deleteMembers(&((UA_ReferenceTypeNode*)node)->inverseName);
  992. UA_LocalizedText_copy((const UA_LocalizedText *)value, &((UA_ReferenceTypeNode*)node)->inverseName);
  993. break;
  994. case UA_ATTRIBUTEID_CONTAINSNOLOOPS:
  995. CHECK_NODECLASS_WRITE(UA_NODECLASS_VIEW);
  996. CHECK_USERWRITEMASK(3);
  997. CHECK_DATATYPE_SCALAR(BOOLEAN);
  998. ((UA_ViewNode*)node)->containsNoLoops = *(const UA_Boolean*)value;
  999. break;
  1000. case UA_ATTRIBUTEID_EVENTNOTIFIER:
  1001. CHECK_NODECLASS_WRITE(UA_NODECLASS_VIEW | UA_NODECLASS_OBJECT);
  1002. CHECK_USERWRITEMASK(7);
  1003. CHECK_DATATYPE_SCALAR(BYTE);
  1004. ((UA_ViewNode*)node)->eventNotifier = *(const UA_Byte*)value;
  1005. break;
  1006. case UA_ATTRIBUTEID_VALUE:
  1007. CHECK_NODECLASS_WRITE(UA_NODECLASS_VARIABLE | UA_NODECLASS_VARIABLETYPE);
  1008. if(node->nodeClass == UA_NODECLASS_VARIABLE) {
  1009. /* The access to a value variable is granted via the AccessLevel Byte */
  1010. UA_Byte userAccessLevel = getUserAccessLevel(server, session, (const UA_VariableNode*)node);
  1011. if(!(userAccessLevel & 0x02)) {
  1012. retval = UA_STATUSCODE_BADUSERACCESSDENIED;
  1013. break;
  1014. }
  1015. } else { /* UA_NODECLASS_VARIABLETYPE */
  1016. CHECK_USERWRITEMASK(21);
  1017. }
  1018. retval = writeValueAttribute(server, (UA_VariableNode*)node,
  1019. &wvalue->value, &wvalue->indexRange);
  1020. break;
  1021. case UA_ATTRIBUTEID_DATATYPE:
  1022. CHECK_NODECLASS_WRITE(UA_NODECLASS_VARIABLE | UA_NODECLASS_VARIABLETYPE);
  1023. CHECK_USERWRITEMASK(4);
  1024. CHECK_DATATYPE_SCALAR(NODEID);
  1025. retval = writeDataTypeAttributeWithVT(server, (UA_VariableNode*)node, (const UA_NodeId*)value);
  1026. break;
  1027. case UA_ATTRIBUTEID_VALUERANK:
  1028. CHECK_NODECLASS_WRITE(UA_NODECLASS_VARIABLE | UA_NODECLASS_VARIABLETYPE);
  1029. CHECK_USERWRITEMASK(19);
  1030. CHECK_DATATYPE_SCALAR(INT32);
  1031. retval = writeValueRankAttributeWithVT(server, (UA_VariableNode*)node, *(const UA_Int32*)value);
  1032. break;
  1033. case UA_ATTRIBUTEID_ARRAYDIMENSIONS:
  1034. CHECK_NODECLASS_WRITE(UA_NODECLASS_VARIABLE | UA_NODECLASS_VARIABLETYPE);
  1035. CHECK_USERWRITEMASK(1);
  1036. CHECK_DATATYPE_ARRAY(UINT32);
  1037. retval = writeArrayDimensionsAttribute(server, (UA_VariableNode*)node,
  1038. wvalue->value.value.arrayLength,
  1039. (UA_UInt32 *)wvalue->value.value.data);
  1040. break;
  1041. case UA_ATTRIBUTEID_ACCESSLEVEL:
  1042. CHECK_NODECLASS_WRITE(UA_NODECLASS_VARIABLE);
  1043. CHECK_USERWRITEMASK(0);
  1044. CHECK_DATATYPE_SCALAR(BYTE);
  1045. ((UA_VariableNode*)node)->accessLevel = *(const UA_Byte*)value;
  1046. break;
  1047. case UA_ATTRIBUTEID_MINIMUMSAMPLINGINTERVAL:
  1048. CHECK_NODECLASS_WRITE(UA_NODECLASS_VARIABLE);
  1049. CHECK_USERWRITEMASK(12);
  1050. CHECK_DATATYPE_SCALAR(DOUBLE);
  1051. ((UA_VariableNode*)node)->minimumSamplingInterval = *(const UA_Double*)value;
  1052. break;
  1053. case UA_ATTRIBUTEID_HISTORIZING:
  1054. CHECK_NODECLASS_WRITE(UA_NODECLASS_VARIABLE);
  1055. CHECK_USERWRITEMASK(9);
  1056. CHECK_DATATYPE_SCALAR(BOOLEAN);
  1057. ((UA_VariableNode*)node)->historizing = *(const UA_Boolean*)value;
  1058. break;
  1059. case UA_ATTRIBUTEID_EXECUTABLE:
  1060. CHECK_NODECLASS_WRITE(UA_NODECLASS_METHOD);
  1061. CHECK_USERWRITEMASK(8);
  1062. CHECK_DATATYPE_SCALAR(BOOLEAN);
  1063. ((UA_MethodNode*)node)->executable = *(const UA_Boolean*)value;
  1064. break;
  1065. default:
  1066. retval = UA_STATUSCODE_BADATTRIBUTEIDINVALID;
  1067. break;
  1068. }
  1069. if(retval != UA_STATUSCODE_GOOD)
  1070. UA_LOG_INFO_SESSION(server->config.logger, session,
  1071. "WriteRequest returned status code 0x%08x", retval);
  1072. return retval;
  1073. }
  1074. void
  1075. Service_Write(UA_Server *server, UA_Session *session,
  1076. const UA_WriteRequest *request, UA_WriteResponse *response) {
  1077. UA_LOG_DEBUG_SESSION(server->config.logger, session, "Processing WriteRequest");
  1078. if(request->nodesToWriteSize <= 0) {
  1079. response->responseHeader.serviceResult = UA_STATUSCODE_BADNOTHINGTODO;
  1080. return;
  1081. }
  1082. response->results = (UA_StatusCode *)UA_Array_new(request->nodesToWriteSize, &UA_TYPES[UA_TYPES_STATUSCODE]);
  1083. if(!response->results) {
  1084. response->responseHeader.serviceResult = UA_STATUSCODE_BADOUTOFMEMORY;
  1085. return;
  1086. }
  1087. response->resultsSize = request->nodesToWriteSize;
  1088. #ifndef UA_ENABLE_EXTERNAL_NAMESPACES
  1089. for(size_t i = 0;i < request->nodesToWriteSize;++i) {
  1090. response->results[i] = UA_Server_editNode(server, session, &request->nodesToWrite[i].nodeId,
  1091. (UA_EditNodeCallback)CopyAttributeIntoNode,
  1092. &request->nodesToWrite[i]);
  1093. }
  1094. #else
  1095. UA_Boolean isExternal[request->nodesToWriteSize];
  1096. UA_UInt32 indices[request->nodesToWriteSize];
  1097. memset(isExternal, false, sizeof(UA_Boolean)*request->nodesToWriteSize);
  1098. for(size_t j = 0; j < server->externalNamespacesSize; ++j) {
  1099. UA_UInt32 indexSize = 0;
  1100. for(size_t i = 0; i < request->nodesToWriteSize; ++i) {
  1101. if(request->nodesToWrite[i].nodeId.namespaceIndex !=
  1102. server->externalNamespaces[j].index)
  1103. continue;
  1104. isExternal[i] = true;
  1105. indices[indexSize] = (UA_UInt32)i;
  1106. ++indexSize;
  1107. }
  1108. if(indexSize == 0)
  1109. continue;
  1110. UA_ExternalNodeStore *ens = &server->externalNamespaces[j].externalNodeStore;
  1111. ens->writeNodes(ens->ensHandle, &request->requestHeader, request->nodesToWrite,
  1112. indices, indexSize, response->results, response->diagnosticInfos);
  1113. }
  1114. for(size_t i = 0;i < request->nodesToWriteSize;++i) {
  1115. if(isExternal[i])
  1116. continue;
  1117. response->results[i] = UA_Server_editNode(server, session, &request->nodesToWrite[i].nodeId,
  1118. (UA_EditNodeCallback)CopyAttributeIntoNode,
  1119. &request->nodesToWrite[i]);
  1120. }
  1121. #endif
  1122. }
  1123. UA_StatusCode
  1124. UA_Server_write(UA_Server *server, const UA_WriteValue *value) {
  1125. UA_RCU_LOCK();
  1126. UA_StatusCode retval =
  1127. UA_Server_editNode(server, &adminSession, &value->nodeId,
  1128. (UA_EditNodeCallback)CopyAttributeIntoNode, value);
  1129. UA_RCU_UNLOCK();
  1130. return retval;
  1131. }
  1132. /* Convenience function to be wrapped into inline functions */
  1133. UA_StatusCode
  1134. __UA_Server_write(UA_Server *server, const UA_NodeId *nodeId,
  1135. const UA_AttributeId attributeId,
  1136. const UA_DataType *attr_type,
  1137. const void *attr) {
  1138. UA_WriteValue wvalue;
  1139. UA_WriteValue_init(&wvalue);
  1140. wvalue.nodeId = *nodeId;
  1141. wvalue.attributeId = attributeId;
  1142. wvalue.value.hasValue = true;
  1143. if(attr_type != &UA_TYPES[UA_TYPES_VARIANT]) {
  1144. /* hacked cast. the target WriteValue is used as const anyway */
  1145. UA_Variant_setScalar(&wvalue.value.value, (void*)(uintptr_t)attr, attr_type);
  1146. } else {
  1147. wvalue.value.value = *(const UA_Variant*)attr;
  1148. }
  1149. return UA_Server_write(server, &wvalue);
  1150. }