ua_services_nodemanagement.c 59 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422
  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. /**********************/
  7. /* Consistency Checks */
  8. /**********************/
  9. /* Check if the requested parent node exists, has the right node class and is
  10. * referenced with an allowed (hierarchical) reference type. For "type" nodes,
  11. * only hasSubType references are allowed. */
  12. static UA_StatusCode
  13. checkParentReference(UA_Server *server, UA_Session *session, UA_NodeClass nodeClass,
  14. const UA_NodeId *parentNodeId, const UA_NodeId *referenceTypeId) {
  15. /* See if the parent exists */
  16. const UA_Node *parent = UA_NodeStore_get(server->nodestore, parentNodeId);
  17. if(!parent) {
  18. UA_LOG_DEBUG_SESSION(server->config.logger, session,
  19. "AddNodes: Parent node not found");
  20. return UA_STATUSCODE_BADPARENTNODEIDINVALID;
  21. }
  22. /* Check the referencetype exists */
  23. const UA_ReferenceTypeNode *referenceType =
  24. (const UA_ReferenceTypeNode*)UA_NodeStore_get(server->nodestore, referenceTypeId);
  25. if(!referenceType) {
  26. UA_LOG_DEBUG_SESSION(server->config.logger, session,
  27. "AddNodes: Reference type to the parent not found");
  28. return UA_STATUSCODE_BADREFERENCETYPEIDINVALID;
  29. }
  30. /* Check if the referencetype is a reference type node */
  31. if(referenceType->nodeClass != UA_NODECLASS_REFERENCETYPE) {
  32. UA_LOG_DEBUG_SESSION(server->config.logger, session,
  33. "AddNodes: Reference type to the parent invalid");
  34. return UA_STATUSCODE_BADREFERENCETYPEIDINVALID;
  35. }
  36. /* Check that the reference type is not abstract */
  37. if(referenceType->isAbstract == true) {
  38. UA_LOG_DEBUG_SESSION(server->config.logger, session,
  39. "AddNodes: Abstract reference type to the parent invalid");
  40. return UA_STATUSCODE_BADREFERENCENOTALLOWED;
  41. }
  42. /* Check hassubtype relation for type nodes */
  43. const UA_NodeId subtypeId = UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE);
  44. if(nodeClass == UA_NODECLASS_DATATYPE ||
  45. nodeClass == UA_NODECLASS_VARIABLETYPE ||
  46. nodeClass == UA_NODECLASS_OBJECTTYPE ||
  47. nodeClass == UA_NODECLASS_REFERENCETYPE) {
  48. /* type needs hassubtype reference to the supertype */
  49. if(!UA_NodeId_equal(referenceTypeId, &subtypeId)) {
  50. UA_LOG_DEBUG_SESSION(server->config.logger, session,
  51. "AddNodes: New type node need to have a "
  52. "hassubtype reference");
  53. return UA_STATUSCODE_BADREFERENCENOTALLOWED;
  54. }
  55. /* supertype needs to be of the same node type */
  56. if(parent->nodeClass != nodeClass) {
  57. UA_LOG_DEBUG_SESSION(server->config.logger, session,
  58. "AddNodes: New type node needs to be of the same "
  59. "node type as the parent");
  60. return UA_STATUSCODE_BADPARENTNODEIDINVALID;
  61. }
  62. return UA_STATUSCODE_GOOD;
  63. }
  64. /* Test if the referencetype is hierarchical */
  65. const UA_NodeId hierarchicalReference =
  66. UA_NODEID_NUMERIC(0, UA_NS0ID_HIERARCHICALREFERENCES);
  67. if(!isNodeInTree(server->nodestore, referenceTypeId,
  68. &hierarchicalReference, &subtypeId, 1)) {
  69. UA_LOG_DEBUG_SESSION(server->config.logger, session,
  70. "AddNodes: Reference type is not hierarchical");
  71. return UA_STATUSCODE_BADREFERENCETYPEIDINVALID;
  72. }
  73. return UA_STATUSCODE_GOOD;
  74. }
  75. /************/
  76. /* Add Node */
  77. /************/
  78. static void
  79. Service_AddNodes_single(UA_Server *server, UA_Session *session,
  80. const UA_AddNodesItem *item, UA_AddNodesResult *result,
  81. UA_InstantiationCallback *instantiationCallback);
  82. static UA_StatusCode
  83. copyChildNodesToNode(UA_Server *server, UA_Session *session,
  84. const UA_NodeId *sourceNodeId, const UA_NodeId *destinationNodeId,
  85. UA_InstantiationCallback *instantiationCallback);
  86. /* copy an existing variable under the given parent. then instantiate the
  87. * variable for its type */
  88. static UA_StatusCode
  89. copyExistingVariable(UA_Server *server, UA_Session *session, const UA_NodeId *variable,
  90. const UA_NodeId *referenceType, const UA_NodeId *parent,
  91. UA_InstantiationCallback *instantiationCallback) {
  92. const UA_VariableNode *node =
  93. (const UA_VariableNode*)UA_NodeStore_get(server->nodestore, variable);
  94. if(!node)
  95. return UA_STATUSCODE_BADNODEIDINVALID;
  96. if(node->nodeClass != UA_NODECLASS_VARIABLE)
  97. return UA_STATUSCODE_BADNODECLASSINVALID;
  98. /* Get the current value */
  99. UA_DataValue value;
  100. UA_DataValue_init(&value);
  101. UA_StatusCode retval = readValueAttribute(server, node, &value);
  102. if(retval != UA_STATUSCODE_GOOD)
  103. return retval;
  104. /* Prepare the variable description */
  105. UA_VariableAttributes attr;
  106. UA_VariableAttributes_init(&attr);
  107. attr.displayName = node->displayName;
  108. attr.description = node->description;
  109. attr.writeMask = node->writeMask;
  110. attr.value = value.value;
  111. attr.dataType = node->dataType;
  112. attr.valueRank = node->valueRank;
  113. attr.arrayDimensionsSize = node->arrayDimensionsSize;
  114. attr.arrayDimensions = node->arrayDimensions;
  115. attr.accessLevel = node->accessLevel;
  116. attr.minimumSamplingInterval = node->minimumSamplingInterval;
  117. attr.historizing = node->historizing;
  118. UA_AddNodesItem item;
  119. UA_AddNodesItem_init(&item);
  120. item.nodeClass = UA_NODECLASS_VARIABLE;
  121. item.parentNodeId.nodeId = *parent;
  122. item.referenceTypeId = *referenceType;
  123. item.browseName = node->browseName;
  124. item.nodeAttributes.encoding = UA_EXTENSIONOBJECT_DECODED_NODELETE;
  125. item.nodeAttributes.content.decoded.type = &UA_TYPES[UA_TYPES_VARIABLEATTRIBUTES];
  126. item.nodeAttributes.content.decoded.data = &attr;
  127. const UA_VariableTypeNode *vt = (const UA_VariableTypeNode*)getNodeType(server, (const UA_Node*)node);
  128. if(!vt || vt->nodeClass != UA_NODECLASS_VARIABLETYPE || vt->isAbstract) {
  129. retval = UA_STATUSCODE_BADTYPEDEFINITIONINVALID;
  130. goto cleanup;
  131. }
  132. item.typeDefinition.nodeId = vt->nodeId;
  133. /* Add the variable and instantiate the children */
  134. UA_AddNodesResult res;
  135. UA_AddNodesResult_init(&res);
  136. Service_AddNodes_single(server, session, &item, &res, instantiationCallback);
  137. if(res.statusCode != UA_STATUSCODE_GOOD) {
  138. retval = res.statusCode;
  139. goto cleanup;
  140. }
  141. retval = copyChildNodesToNode(server, session, &node->nodeId,
  142. &res.addedNodeId, instantiationCallback);
  143. if(retval == UA_STATUSCODE_GOOD && instantiationCallback)
  144. instantiationCallback->method(res.addedNodeId, node->nodeId,
  145. instantiationCallback->handle);
  146. UA_NodeId_deleteMembers(&res.addedNodeId);
  147. cleanup:
  148. if(value.hasValue && value.value.storageType == UA_VARIANT_DATA)
  149. UA_Variant_deleteMembers(&value.value);
  150. return retval;
  151. }
  152. /* Copy an existing object under the given parent. Then instantiate for all
  153. * hastypedefinitions of the original version. */
  154. static UA_StatusCode
  155. copyExistingObject(UA_Server *server, UA_Session *session, const UA_NodeId *object,
  156. const UA_NodeId *referenceType, const UA_NodeId *parent,
  157. UA_InstantiationCallback *instantiationCallback) {
  158. const UA_ObjectNode *node =
  159. (const UA_ObjectNode*)UA_NodeStore_get(server->nodestore, object);
  160. if(!node)
  161. return UA_STATUSCODE_BADNODEIDINVALID;
  162. if(node->nodeClass != UA_NODECLASS_OBJECT)
  163. return UA_STATUSCODE_BADNODECLASSINVALID;
  164. /* Prepare the item */
  165. UA_ObjectAttributes attr;
  166. UA_ObjectAttributes_init(&attr);
  167. attr.displayName = node->displayName;
  168. attr.description = node->description;
  169. attr.writeMask = node->writeMask;
  170. attr.eventNotifier = node->eventNotifier;
  171. UA_AddNodesItem item;
  172. UA_AddNodesItem_init(&item);
  173. item.nodeClass = UA_NODECLASS_OBJECT;
  174. item.parentNodeId.nodeId = *parent;
  175. item.referenceTypeId = *referenceType;
  176. item.browseName = node->browseName;
  177. item.nodeAttributes.encoding = UA_EXTENSIONOBJECT_DECODED_NODELETE;
  178. item.nodeAttributes.content.decoded.type = &UA_TYPES[UA_TYPES_OBJECTATTRIBUTES];
  179. item.nodeAttributes.content.decoded.data = &attr;
  180. const UA_ObjectTypeNode *objtype = (const UA_ObjectTypeNode*)getNodeType(server, (const UA_Node*)node);
  181. if(!objtype || objtype->nodeClass != UA_NODECLASS_OBJECTTYPE || objtype->isAbstract)
  182. return UA_STATUSCODE_BADTYPEDEFINITIONINVALID;
  183. item.typeDefinition.nodeId = objtype->nodeId;
  184. /* add the new object */
  185. UA_AddNodesResult res;
  186. UA_AddNodesResult_init(&res);
  187. Service_AddNodes_single(server, session, &item, &res, instantiationCallback);
  188. if(res.statusCode != UA_STATUSCODE_GOOD)
  189. return res.statusCode;
  190. /* Copy any aggregated/nested variables/methods/subobjects this object contains
  191. * These objects may not be part of the nodes type. */
  192. UA_StatusCode retval = copyChildNodesToNode(server, session, &node->nodeId,
  193. &res.addedNodeId, instantiationCallback);
  194. if(retval == UA_STATUSCODE_GOOD && instantiationCallback)
  195. instantiationCallback->method(res.addedNodeId, node->nodeId,
  196. instantiationCallback->handle);
  197. UA_NodeId_deleteMembers(&res.addedNodeId);
  198. return retval;
  199. }
  200. static UA_StatusCode
  201. setObjectInstanceHandle(UA_Server *server, UA_Session *session,
  202. UA_ObjectNode* node, void * (*constructor)(const UA_NodeId instance)) {
  203. if(node->nodeClass != UA_NODECLASS_OBJECT)
  204. return UA_STATUSCODE_BADNODECLASSINVALID;
  205. if(!node->instanceHandle)
  206. node->instanceHandle = constructor(node->nodeId);
  207. return UA_STATUSCODE_GOOD;
  208. }
  209. static UA_StatusCode
  210. instantiateNode(UA_Server *server, UA_Session *session, const UA_NodeId *nodeId,
  211. UA_NodeClass nodeClass, const UA_NodeId *typeId,
  212. UA_InstantiationCallback *instantiationCallback) {
  213. /* see if the type node is correct */
  214. const UA_Node *typenode = UA_NodeStore_get(server->nodestore, typeId);
  215. if(!typenode)
  216. return UA_STATUSCODE_BADTYPEDEFINITIONINVALID;
  217. if(nodeClass == UA_NODECLASS_VARIABLE) {
  218. if(typenode->nodeClass != UA_NODECLASS_VARIABLETYPE ||
  219. ((const UA_VariableTypeNode*)typenode)->isAbstract)
  220. return UA_STATUSCODE_BADTYPEDEFINITIONINVALID;
  221. } else if(nodeClass == UA_NODECLASS_OBJECT) {
  222. if(typenode->nodeClass != UA_NODECLASS_OBJECTTYPE ||
  223. ((const UA_ObjectTypeNode*)typenode)->isAbstract)
  224. return UA_STATUSCODE_BADTYPEDEFINITIONINVALID;
  225. } else {
  226. return UA_STATUSCODE_BADTYPEDEFINITIONINVALID;
  227. }
  228. /* Get the hierarchy of the type and all its supertypes */
  229. UA_NodeId *hierarchy = NULL;
  230. size_t hierarchySize = 0;
  231. UA_StatusCode retval =
  232. getTypeHierarchy(server->nodestore, typenode, true, &hierarchy, &hierarchySize);
  233. if(retval != UA_STATUSCODE_GOOD)
  234. return retval;
  235. /* Copy members of the type and supertypes */
  236. for(size_t i = 0; i < hierarchySize; ++i)
  237. retval |= copyChildNodesToNode(server, session, &hierarchy[i], nodeId, instantiationCallback);
  238. UA_Array_delete(hierarchy, hierarchySize, &UA_TYPES[UA_TYPES_NODEID]);
  239. if(retval != UA_STATUSCODE_GOOD)
  240. return retval;
  241. /* Call the object constructor */
  242. if(typenode->nodeClass == UA_NODECLASS_OBJECTTYPE) {
  243. const UA_ObjectLifecycleManagement *olm =
  244. &((const UA_ObjectTypeNode*)typenode)->lifecycleManagement;
  245. if(olm->constructor)
  246. UA_Server_editNode(server, session, nodeId,
  247. (UA_EditNodeCallback)setObjectInstanceHandle,
  248. (void*)olm->constructor);
  249. }
  250. /* Add a hasType reference */
  251. UA_AddReferencesItem addref;
  252. UA_AddReferencesItem_init(&addref);
  253. addref.sourceNodeId = *nodeId;
  254. addref.referenceTypeId = UA_NODEID_NUMERIC(0, UA_NS0ID_HASTYPEDEFINITION);
  255. addref.isForward = true;
  256. addref.targetNodeId.nodeId = *typeId;
  257. return Service_AddReferences_single(server, session, &addref);
  258. }
  259. /* Search for an instance of "browseName" in node searchInstance
  260. * Used during copyChildNodes to find overwritable/mergable nodes */
  261. static UA_StatusCode
  262. instanceFindAggregateByBrowsename(UA_Server *server, UA_Session *session,
  263. const UA_NodeId *searchInstance,
  264. const UA_QualifiedName *browseName,
  265. UA_NodeId *outInstanceNodeId) {
  266. UA_BrowseDescription bd;
  267. UA_BrowseDescription_init(&bd);
  268. bd.nodeId = *searchInstance;
  269. bd.referenceTypeId = UA_NODEID_NUMERIC(0, UA_NS0ID_AGGREGATES);
  270. bd.includeSubtypes = true;
  271. bd.browseDirection = UA_BROWSEDIRECTION_FORWARD;
  272. bd.nodeClassMask = UA_NODECLASS_OBJECT | UA_NODECLASS_VARIABLE | UA_NODECLASS_METHOD;
  273. bd.resultMask = UA_BROWSERESULTMASK_NODECLASS | UA_BROWSERESULTMASK_BROWSENAME;
  274. UA_BrowseResult br;
  275. UA_BrowseResult_init(&br);
  276. Service_Browse_single(server, session, NULL, &bd, 0, &br);
  277. if(br.statusCode != UA_STATUSCODE_GOOD)
  278. return br.statusCode;
  279. UA_StatusCode retval = UA_STATUSCODE_GOOD;
  280. for(size_t i = 0; i < br.referencesSize; ++i) {
  281. UA_ReferenceDescription *rd = &br.references[i];
  282. if(rd->browseName.namespaceIndex == browseName->namespaceIndex &&
  283. UA_String_equal(&rd->browseName.name, &browseName->name)) {
  284. retval = UA_NodeId_copy(&rd->nodeId.nodeId, outInstanceNodeId);
  285. break;
  286. }
  287. }
  288. UA_BrowseResult_deleteMembers(&br);
  289. return retval;
  290. }
  291. /* Copy any children of Node sourceNodeId to another node destinationNodeId
  292. * Used at 2 places:
  293. * (1) During instantiation, when any children of the Type are copied
  294. * (2) During instantiation to copy any *nested* instances to the new node
  295. * (2.1) Might call instantiation of a type first
  296. * (2.2) *Should* then overwrite nested contents in definition --> this scenario is currently not handled!
  297. */
  298. static UA_StatusCode
  299. copyChildNodesToNode(UA_Server* server, UA_Session* session,
  300. const UA_NodeId* sourceNodeId, const UA_NodeId* destinationNodeId,
  301. UA_InstantiationCallback* instantiationCallback) {
  302. /* Browse to get all children */
  303. UA_BrowseDescription bd;
  304. UA_BrowseDescription_init(&bd);
  305. bd.nodeId = *sourceNodeId;
  306. bd.referenceTypeId = UA_NODEID_NUMERIC(0, UA_NS0ID_AGGREGATES);
  307. bd.includeSubtypes = true;
  308. bd.browseDirection = UA_BROWSEDIRECTION_FORWARD;
  309. bd.nodeClassMask = UA_NODECLASS_OBJECT | UA_NODECLASS_VARIABLE | UA_NODECLASS_METHOD;
  310. bd.resultMask = UA_BROWSERESULTMASK_REFERENCETYPEID | UA_BROWSERESULTMASK_NODECLASS |
  311. UA_BROWSERESULTMASK_BROWSENAME;
  312. UA_BrowseResult br;
  313. UA_BrowseResult_init(&br);
  314. Service_Browse_single(server, session, NULL, &bd, 0, &br);
  315. if(br.statusCode != UA_STATUSCODE_GOOD)
  316. return br.statusCode;
  317. /* Copy all children */
  318. UA_StatusCode retval = UA_STATUSCODE_GOOD;
  319. UA_NodeId existingChild = UA_NODEID_NULL;
  320. for(size_t i = 0; i < br.referencesSize; ++i) {
  321. UA_ReferenceDescription *rd = &br.references[i];
  322. // Check for deduplication
  323. retval = instanceFindAggregateByBrowsename(server, session, destinationNodeId,
  324. &rd->browseName, &existingChild);
  325. if(retval != UA_STATUSCODE_GOOD)
  326. break;
  327. if(UA_NodeId_equal(&UA_NODEID_NULL, &existingChild)) {
  328. /* New node in child */
  329. if(rd->nodeClass == UA_NODECLASS_METHOD) {
  330. /* add a reference to the method in the objecttype */
  331. UA_AddReferencesItem newItem;
  332. UA_AddReferencesItem_init(&newItem);
  333. newItem.sourceNodeId = *destinationNodeId;
  334. newItem.referenceTypeId = rd->referenceTypeId;
  335. newItem.isForward = true;
  336. newItem.targetNodeId = rd->nodeId;
  337. newItem.targetNodeClass = UA_NODECLASS_METHOD;
  338. retval = Service_AddReferences_single(server, session, &newItem);
  339. } else if(rd->nodeClass == UA_NODECLASS_VARIABLE)
  340. retval = copyExistingVariable(server, session, &rd->nodeId.nodeId,
  341. &rd->referenceTypeId, destinationNodeId,
  342. instantiationCallback);
  343. else if(rd->nodeClass == UA_NODECLASS_OBJECT)
  344. retval = copyExistingObject(server, session, &rd->nodeId.nodeId,
  345. &rd->referenceTypeId, destinationNodeId,
  346. instantiationCallback);
  347. } else {
  348. /* Preexistent node in child
  349. * General strategy if we meet an already existing node:
  350. * - Preexistent variable contents always 'win' overwriting anything
  351. * supertypes would instantiate
  352. * - Always copy contents of template *into* existant node (merge
  353. * contents of e.g. Folders like ParameterSet) */
  354. if(rd->nodeClass == UA_NODECLASS_METHOD) {
  355. /* Do nothing, existent method wins */
  356. } else if(rd->nodeClass == UA_NODECLASS_VARIABLE ||
  357. rd->nodeClass == UA_NODECLASS_OBJECT) {
  358. if(!UA_NodeId_equal(&rd->nodeId.nodeId, &existingChild))
  359. retval = copyChildNodesToNode(server, session, &rd->nodeId.nodeId,
  360. &existingChild, instantiationCallback);
  361. }
  362. UA_NodeId_deleteMembers(&existingChild);
  363. }
  364. if(retval != UA_STATUSCODE_GOOD)
  365. break;
  366. }
  367. UA_BrowseResult_deleteMembers(&br);
  368. return retval;
  369. }
  370. UA_StatusCode
  371. Service_AddNodes_existing(UA_Server *server, UA_Session *session, UA_Node *node,
  372. const UA_NodeId *parentNodeId, const UA_NodeId *referenceTypeId,
  373. const UA_NodeId *typeDefinition,
  374. UA_InstantiationCallback *instantiationCallback,
  375. UA_NodeId *addedNodeId) {
  376. UA_ASSERT_RCU_LOCKED();
  377. /* Check the namespaceindex */
  378. if(node->nodeId.namespaceIndex >= server->namespacesSize) {
  379. UA_LOG_DEBUG_SESSION(server->config.logger, session, "AddNodes: Namespace invalid");
  380. UA_NodeStore_deleteNode(node);
  381. return UA_STATUSCODE_BADNODEIDINVALID;
  382. }
  383. /* Check the reference to the parent */
  384. UA_StatusCode retval = checkParentReference(server, session, node->nodeClass,
  385. parentNodeId, referenceTypeId);
  386. if(retval != UA_STATUSCODE_GOOD) {
  387. UA_LOG_DEBUG_SESSION(server->config.logger, session,
  388. "AddNodes: Checking the reference to the parent returned"
  389. "error code %s", UA_StatusCode_name(retval));
  390. UA_NodeStore_deleteNode(node);
  391. return retval;
  392. }
  393. /* Add the node to the nodestore */
  394. retval = UA_NodeStore_insert(server->nodestore, node);
  395. if(retval != UA_STATUSCODE_GOOD) {
  396. UA_LOG_DEBUG_SESSION(server->config.logger, session,
  397. "AddNodes: Node could not be added to the nodestore "
  398. "with error code %s", UA_StatusCode_name(retval));
  399. return retval;
  400. }
  401. /* Copy the nodeid if needed */
  402. if(addedNodeId) {
  403. retval = UA_NodeId_copy(&node->nodeId, addedNodeId);
  404. if(retval != UA_STATUSCODE_GOOD) {
  405. UA_LOG_DEBUG_SESSION(server->config.logger, session,
  406. "AddNodes: Could not copy the nodeid");
  407. goto remove_node;
  408. }
  409. }
  410. /* Hierarchical reference back to the parent */
  411. UA_AddReferencesItem item;
  412. UA_AddReferencesItem_init(&item);
  413. item.sourceNodeId = node->nodeId;
  414. item.referenceTypeId = *referenceTypeId;
  415. item.isForward = false;
  416. item.targetNodeId.nodeId = *parentNodeId;
  417. retval = Service_AddReferences_single(server, session, &item);
  418. if(retval != UA_STATUSCODE_GOOD) {
  419. UA_LOG_DEBUG_SESSION(server->config.logger, session,
  420. "AddNodes: Could not add the reference to the parent"
  421. "with error code %s", UA_StatusCode_name(retval));
  422. goto remove_node;
  423. }
  424. if(node->nodeClass == UA_NODECLASS_VARIABLE ||
  425. node->nodeClass == UA_NODECLASS_OBJECT) {
  426. /* Fall back to a default typedefinition for variables and objects */
  427. const UA_NodeId basedatavariabletype = UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE);
  428. const UA_NodeId baseobjecttype = UA_NODEID_NUMERIC(0, UA_NS0ID_BASEOBJECTTYPE);
  429. if(!typeDefinition || UA_NodeId_isNull(typeDefinition)) {
  430. if(node->nodeClass == UA_NODECLASS_VARIABLE)
  431. typeDefinition = &basedatavariabletype;
  432. else
  433. typeDefinition = &baseobjecttype;
  434. }
  435. /* Instantiate variables and objects */
  436. retval = instantiateNode(server, session, &node->nodeId, node->nodeClass,
  437. typeDefinition, instantiationCallback);
  438. if(retval != UA_STATUSCODE_GOOD) {
  439. UA_LOG_DEBUG_SESSION(server->config.logger, session,
  440. "AddNodes: Could not instantiate the node with"
  441. "error code 0x%08x", retval);
  442. goto remove_node;
  443. }
  444. }
  445. /* Custom callback */
  446. if(instantiationCallback)
  447. instantiationCallback->method(node->nodeId, *typeDefinition,
  448. instantiationCallback->handle);
  449. return UA_STATUSCODE_GOOD;
  450. remove_node:
  451. Service_DeleteNodes_single(server, &adminSession, &node->nodeId, true);
  452. return retval;
  453. }
  454. /*******************************************/
  455. /* Create nodes from attribute description */
  456. /*******************************************/
  457. static UA_StatusCode
  458. copyStandardAttributes(UA_Node *node, const UA_AddNodesItem *item,
  459. const UA_NodeAttributes *attr) {
  460. UA_StatusCode retval;
  461. retval = UA_NodeId_copy(&item->requestedNewNodeId.nodeId, &node->nodeId);
  462. retval |= UA_QualifiedName_copy(&item->browseName, &node->browseName);
  463. retval |= UA_LocalizedText_copy(&attr->displayName, &node->displayName);
  464. retval |= UA_LocalizedText_copy(&attr->description, &node->description);
  465. node->writeMask = attr->writeMask;
  466. return retval;
  467. }
  468. static UA_StatusCode
  469. copyCommonVariableAttributes(UA_Server *server, UA_VariableNode *node,
  470. const UA_AddNodesItem *item,
  471. const UA_VariableAttributes *attr) {
  472. const UA_NodeId basevartype = UA_NODEID_NUMERIC(0, UA_NS0ID_BASEVARIABLETYPE);
  473. const UA_NodeId basedatavartype = UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE);
  474. const UA_NodeId *typeDef = &item->typeDefinition.nodeId;
  475. if(UA_NodeId_isNull(typeDef)) /* workaround when the variabletype is undefined */
  476. typeDef = &basedatavartype;
  477. /* Make sure we can instantiate the basetypes themselves */
  478. UA_StatusCode retval = UA_STATUSCODE_GOOD;
  479. if(UA_NodeId_equal(&node->nodeId, &basevartype) ||
  480. UA_NodeId_equal(&node->nodeId, &basedatavartype)) {
  481. node->dataType = UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATATYPE);
  482. node->valueRank = -2;
  483. return retval;
  484. }
  485. const UA_VariableTypeNode *vt =
  486. (const UA_VariableTypeNode*)UA_NodeStore_get(server->nodestore, typeDef);
  487. if(!vt || vt->nodeClass != UA_NODECLASS_VARIABLETYPE)
  488. return UA_STATUSCODE_BADTYPEDEFINITIONINVALID;
  489. if(node->nodeClass == UA_NODECLASS_VARIABLE && vt->isAbstract)
  490. return UA_STATUSCODE_BADTYPEDEFINITIONINVALID;
  491. /* Set the datatype */
  492. if(!UA_NodeId_isNull(&attr->dataType))
  493. retval = writeDataTypeAttribute(server, node, &attr->dataType, &vt->dataType);
  494. else /* workaround common error where the datatype is left as NA_NODEID_NULL */
  495. retval = UA_NodeId_copy(&vt->dataType, &node->dataType);
  496. if(retval != UA_STATUSCODE_GOOD)
  497. return retval;
  498. /* Set the array dimensions. Check only against the vt. */
  499. retval = compatibleArrayDimensions(attr->arrayDimensionsSize, attr->arrayDimensions,
  500. vt->arrayDimensionsSize, vt->arrayDimensions);
  501. if(retval != UA_STATUSCODE_GOOD)
  502. return retval;
  503. retval = UA_Array_copy(attr->arrayDimensions, attr->arrayDimensionsSize,
  504. (void**)&node->arrayDimensions, &UA_TYPES[UA_TYPES_UINT32]);
  505. if(retval != UA_STATUSCODE_GOOD)
  506. return retval;
  507. node->arrayDimensionsSize = attr->arrayDimensionsSize;
  508. /* Set the valuerank */
  509. if(attr->valueRank != 0 || !UA_Variant_isScalar(&attr->value))
  510. retval = writeValueRankAttribute(server, node, attr->valueRank, vt->valueRank);
  511. else /* workaround common error where the valuerank is left as 0 */
  512. node->valueRank = vt->valueRank;
  513. if(retval != UA_STATUSCODE_GOOD)
  514. return retval;
  515. /* Set the value */
  516. UA_DataValue value;
  517. UA_DataValue_init(&value);
  518. value.value = attr->value;
  519. value.hasValue = true;
  520. retval |= writeValueAttribute(server, node, &value, NULL);
  521. return retval;
  522. }
  523. static UA_StatusCode
  524. copyVariableNodeAttributes(UA_Server *server, UA_VariableNode *vnode,
  525. const UA_AddNodesItem *item,
  526. const UA_VariableAttributes *attr) {
  527. vnode->accessLevel = attr->accessLevel;
  528. vnode->historizing = attr->historizing;
  529. vnode->minimumSamplingInterval = attr->minimumSamplingInterval;
  530. return copyCommonVariableAttributes(server, vnode, item, attr);
  531. }
  532. static UA_StatusCode
  533. copyVariableTypeNodeAttributes(UA_Server *server, UA_VariableTypeNode *vtnode,
  534. const UA_AddNodesItem *item,
  535. const UA_VariableTypeAttributes *attr) {
  536. vtnode->isAbstract = attr->isAbstract;
  537. return copyCommonVariableAttributes(server, (UA_VariableNode*)vtnode, item,
  538. (const UA_VariableAttributes*)attr);
  539. }
  540. static UA_StatusCode
  541. copyObjectNodeAttributes(UA_ObjectNode *onode, const UA_ObjectAttributes *attr) {
  542. onode->eventNotifier = attr->eventNotifier;
  543. return UA_STATUSCODE_GOOD;
  544. }
  545. static UA_StatusCode
  546. copyReferenceTypeNodeAttributes(UA_ReferenceTypeNode *rtnode,
  547. const UA_ReferenceTypeAttributes *attr) {
  548. rtnode->isAbstract = attr->isAbstract;
  549. rtnode->symmetric = attr->symmetric;
  550. return UA_LocalizedText_copy(&attr->inverseName, &rtnode->inverseName);
  551. }
  552. static UA_StatusCode
  553. copyObjectTypeNodeAttributes(UA_ObjectTypeNode *otnode,
  554. const UA_ObjectTypeAttributes *attr) {
  555. otnode->isAbstract = attr->isAbstract;
  556. return UA_STATUSCODE_GOOD;
  557. }
  558. static UA_StatusCode
  559. copyViewNodeAttributes(UA_ViewNode *vnode, const UA_ViewAttributes *attr) {
  560. vnode->containsNoLoops = attr->containsNoLoops;
  561. vnode->eventNotifier = attr->eventNotifier;
  562. return UA_STATUSCODE_GOOD;
  563. }
  564. static UA_StatusCode
  565. copyDataTypeNodeAttributes(UA_DataTypeNode *dtnode,
  566. const UA_DataTypeAttributes *attr) {
  567. dtnode->isAbstract = attr->isAbstract;
  568. return UA_STATUSCODE_GOOD;
  569. }
  570. #define CHECK_ATTRIBUTES(TYPE) \
  571. if(item->nodeAttributes.content.decoded.type != &UA_TYPES[UA_TYPES_##TYPE]) { \
  572. retval = UA_STATUSCODE_BADNODEATTRIBUTESINVALID; \
  573. break; \
  574. }
  575. static UA_StatusCode
  576. createNodeFromAttributes(UA_Server *server, const UA_AddNodesItem *item, UA_Node **newNode) {
  577. /* Check that we can read the attributes */
  578. if(item->nodeAttributes.encoding < UA_EXTENSIONOBJECT_DECODED ||
  579. !item->nodeAttributes.content.decoded.type)
  580. return UA_STATUSCODE_BADNODEATTRIBUTESINVALID;
  581. /* Create the node */
  582. // todo: error case where the nodeclass is faulty
  583. void *node = UA_NodeStore_newNode(item->nodeClass);
  584. if(!node)
  585. return UA_STATUSCODE_BADOUTOFMEMORY;
  586. /* Copy the attributes into the node */
  587. void *data = item->nodeAttributes.content.decoded.data;
  588. UA_StatusCode retval = copyStandardAttributes((UA_Node *)node, item, (UA_NodeAttributes *)data);
  589. switch(item->nodeClass) {
  590. case UA_NODECLASS_OBJECT:
  591. CHECK_ATTRIBUTES(OBJECTATTRIBUTES);
  592. retval |= copyObjectNodeAttributes((UA_ObjectNode *)node, (UA_ObjectAttributes *)data);
  593. break;
  594. case UA_NODECLASS_VARIABLE:
  595. CHECK_ATTRIBUTES(VARIABLEATTRIBUTES);
  596. retval |= copyVariableNodeAttributes(server, (UA_VariableNode *)node, item, (UA_VariableAttributes *)data);
  597. break;
  598. case UA_NODECLASS_OBJECTTYPE:
  599. CHECK_ATTRIBUTES(OBJECTTYPEATTRIBUTES);
  600. retval |= copyObjectTypeNodeAttributes((UA_ObjectTypeNode *)node, (UA_ObjectTypeAttributes *)data);
  601. break;
  602. case UA_NODECLASS_VARIABLETYPE:
  603. CHECK_ATTRIBUTES(VARIABLETYPEATTRIBUTES);
  604. retval |= copyVariableTypeNodeAttributes(server,(UA_VariableTypeNode *)node, item, (UA_VariableTypeAttributes *)data);
  605. break;
  606. case UA_NODECLASS_REFERENCETYPE:
  607. CHECK_ATTRIBUTES(REFERENCETYPEATTRIBUTES);
  608. retval |= copyReferenceTypeNodeAttributes((UA_ReferenceTypeNode *)node,(const UA_ReferenceTypeAttributes *)data);
  609. break;
  610. case UA_NODECLASS_DATATYPE:
  611. CHECK_ATTRIBUTES(DATATYPEATTRIBUTES);
  612. retval |= copyDataTypeNodeAttributes((UA_DataTypeNode *)node, (UA_DataTypeAttributes *)data);
  613. break;
  614. case UA_NODECLASS_VIEW:
  615. CHECK_ATTRIBUTES(VIEWATTRIBUTES);
  616. retval |= copyViewNodeAttributes((UA_ViewNode *)node, (const UA_ViewAttributes *)data);
  617. break;
  618. case UA_NODECLASS_METHOD:
  619. case UA_NODECLASS_UNSPECIFIED:
  620. default:
  621. retval = UA_STATUSCODE_BADNODECLASSINVALID;
  622. }
  623. if(retval == UA_STATUSCODE_GOOD)
  624. *newNode = (UA_Node*)node;
  625. else
  626. UA_NodeStore_deleteNode((UA_Node*)node);
  627. return retval;
  628. }
  629. static void
  630. Service_AddNodes_single(UA_Server *server, UA_Session *session,
  631. const UA_AddNodesItem *item, UA_AddNodesResult *result,
  632. UA_InstantiationCallback *instantiationCallback) {
  633. /* Create the node from the attributes*/
  634. UA_Node *node = NULL;
  635. result->statusCode = createNodeFromAttributes(server, item, &node);
  636. if(result->statusCode != UA_STATUSCODE_GOOD)
  637. return;
  638. /* Run consistency checks and add the node */
  639. UA_assert(node != NULL);
  640. result->statusCode = Service_AddNodes_existing(server, session, node, &item->parentNodeId.nodeId,
  641. &item->referenceTypeId, &item->typeDefinition.nodeId,
  642. instantiationCallback, &result->addedNodeId);
  643. }
  644. void Service_AddNodes(UA_Server *server, UA_Session *session,
  645. const UA_AddNodesRequest *request,
  646. UA_AddNodesResponse *response) {
  647. UA_LOG_DEBUG_SESSION(server->config.logger, session, "Processing AddNodesRequest");
  648. if(request->nodesToAddSize <= 0) {
  649. response->responseHeader.serviceResult = UA_STATUSCODE_BADNOTHINGTODO;
  650. return;
  651. }
  652. size_t size = request->nodesToAddSize;
  653. response->results = (UA_AddNodesResult *)UA_Array_new(size, &UA_TYPES[UA_TYPES_ADDNODESRESULT]);
  654. if(!response->results) {
  655. response->responseHeader.serviceResult = UA_STATUSCODE_BADOUTOFMEMORY;
  656. return;
  657. }
  658. #ifdef UA_ENABLE_EXTERNAL_NAMESPACES
  659. #ifdef _MSC_VER
  660. UA_Boolean *isExternal = UA_alloca(size);
  661. UA_UInt32 *indices = UA_alloca(sizeof(UA_UInt32)*size);
  662. #else
  663. UA_Boolean isExternal[size];
  664. UA_UInt32 indices[size];
  665. #endif
  666. memset(isExternal, false, sizeof(UA_Boolean) * size);
  667. for(size_t j = 0; j <server->externalNamespacesSize; ++j) {
  668. size_t indexSize = 0;
  669. for(size_t i = 0;i < size;++i) {
  670. if(request->nodesToAdd[i].requestedNewNodeId.nodeId.namespaceIndex !=
  671. server->externalNamespaces[j].index)
  672. continue;
  673. isExternal[i] = true;
  674. indices[indexSize] = (UA_UInt32)i;
  675. ++indexSize;
  676. }
  677. if(indexSize == 0)
  678. continue;
  679. UA_ExternalNodeStore *ens = &server->externalNamespaces[j].externalNodeStore;
  680. ens->addNodes(ens->ensHandle, &request->requestHeader,
  681. request->nodesToAdd, indices, (UA_UInt32)indexSize,
  682. response->results, response->diagnosticInfos);
  683. }
  684. #endif
  685. response->resultsSize = size;
  686. for(size_t i = 0; i < size; ++i) {
  687. #ifdef UA_ENABLE_EXTERNAL_NAMESPACES
  688. if(!isExternal[i])
  689. #endif
  690. Service_AddNodes_single(server, session, &request->nodesToAdd[i],
  691. &response->results[i], NULL);
  692. }
  693. }
  694. UA_StatusCode
  695. __UA_Server_addNode(UA_Server *server, const UA_NodeClass nodeClass,
  696. const UA_NodeId requestedNewNodeId, const UA_NodeId parentNodeId,
  697. const UA_NodeId referenceTypeId, const UA_QualifiedName browseName,
  698. const UA_NodeId typeDefinition, const UA_NodeAttributes *attr,
  699. const UA_DataType *attributeType,
  700. UA_InstantiationCallback *instantiationCallback, UA_NodeId *outNewNodeId) {
  701. /* prepare the item */
  702. UA_AddNodesItem item;
  703. UA_AddNodesItem_init(&item);
  704. item.parentNodeId.nodeId = parentNodeId;
  705. item.referenceTypeId = referenceTypeId;
  706. item.requestedNewNodeId.nodeId = requestedNewNodeId;
  707. item.browseName = browseName;
  708. item.nodeClass = nodeClass;
  709. item.typeDefinition.nodeId = typeDefinition;
  710. item.nodeAttributes.encoding = UA_EXTENSIONOBJECT_DECODED_NODELETE;
  711. item.nodeAttributes.content.decoded.type = attributeType;
  712. item.nodeAttributes.content.decoded.data = (void*)(uintptr_t)attr;
  713. /* run the service */
  714. UA_AddNodesResult result;
  715. UA_AddNodesResult_init(&result);
  716. UA_RCU_LOCK();
  717. Service_AddNodes_single(server, &adminSession, &item, &result, instantiationCallback);
  718. UA_RCU_UNLOCK();
  719. /* prepare the output */
  720. if(outNewNodeId && result.statusCode == UA_STATUSCODE_GOOD)
  721. *outNewNodeId = result.addedNodeId;
  722. else
  723. UA_NodeId_deleteMembers(&result.addedNodeId);
  724. return result.statusCode;
  725. }
  726. /**************************************************/
  727. /* Add Special Nodes (not possible over the wire) */
  728. /**************************************************/
  729. UA_StatusCode
  730. UA_Server_addDataSourceVariableNode(UA_Server *server, const UA_NodeId requestedNewNodeId,
  731. const UA_NodeId parentNodeId, const UA_NodeId referenceTypeId,
  732. const UA_QualifiedName browseName, const UA_NodeId typeDefinition,
  733. const UA_VariableAttributes attr, const UA_DataSource dataSource,
  734. UA_NodeId *outNewNodeId) {
  735. /* Create the new node */
  736. UA_VariableNode *node = UA_NodeStore_newVariableNode();
  737. if(!node)
  738. return UA_STATUSCODE_BADOUTOFMEMORY;
  739. /* Read the current value (to do typechecking) */
  740. UA_StatusCode retval = UA_STATUSCODE_GOOD;
  741. UA_VariableAttributes editAttr = attr;
  742. UA_DataValue value;
  743. UA_DataValue_init(&value);
  744. if(dataSource.read)
  745. retval = dataSource.read(dataSource.handle, requestedNewNodeId,
  746. false, NULL, &value);
  747. else
  748. retval = UA_STATUSCODE_BADTYPEMISMATCH;
  749. editAttr.value = value.value;
  750. if(retval != UA_STATUSCODE_GOOD) {
  751. UA_NodeStore_deleteNode((UA_Node*)node);
  752. return retval;
  753. }
  754. /* Copy attributes into node */
  755. UA_RCU_LOCK();
  756. UA_AddNodesItem item;
  757. UA_AddNodesItem_init(&item);
  758. item.requestedNewNodeId.nodeId = requestedNewNodeId;
  759. item.browseName = browseName;
  760. item.typeDefinition.nodeId = typeDefinition;
  761. item.parentNodeId.nodeId = parentNodeId;
  762. retval |= copyStandardAttributes((UA_Node*)node, &item, (const UA_NodeAttributes*)&editAttr);
  763. retval |= copyVariableNodeAttributes(server, node, &item, &editAttr);
  764. UA_DataValue_deleteMembers(&node->value.data.value);
  765. node->valueSource = UA_VALUESOURCE_DATASOURCE;
  766. node->value.dataSource = dataSource;
  767. UA_DataValue_deleteMembers(&value);
  768. if(retval != UA_STATUSCODE_GOOD) {
  769. UA_NodeStore_deleteNode((UA_Node*)node);
  770. UA_RCU_UNLOCK();
  771. return retval;
  772. }
  773. /* Add the node */
  774. UA_AddNodesResult result;
  775. UA_AddNodesResult_init(&result);
  776. retval = Service_AddNodes_existing(server, &adminSession, (UA_Node*)node, &parentNodeId,
  777. &referenceTypeId, &typeDefinition, NULL, outNewNodeId);
  778. UA_RCU_UNLOCK();
  779. return retval;
  780. }
  781. #ifdef UA_ENABLE_METHODCALLS
  782. UA_StatusCode
  783. UA_Server_addMethodNode(UA_Server *server, const UA_NodeId requestedNewNodeId,
  784. const UA_NodeId parentNodeId, const UA_NodeId referenceTypeId,
  785. const UA_QualifiedName browseName, const UA_MethodAttributes attr,
  786. UA_MethodCallback method, void *handle,
  787. size_t inputArgumentsSize, const UA_Argument* inputArguments,
  788. size_t outputArgumentsSize, const UA_Argument* outputArguments,
  789. UA_NodeId *outNewNodeId) {
  790. UA_MethodNode *node = UA_NodeStore_newMethodNode();
  791. if(!node)
  792. return UA_STATUSCODE_BADOUTOFMEMORY;
  793. UA_AddNodesItem item;
  794. UA_AddNodesItem_init(&item);
  795. item.requestedNewNodeId.nodeId = requestedNewNodeId;
  796. item.browseName = browseName;
  797. copyStandardAttributes((UA_Node*)node, &item, (const UA_NodeAttributes*)&attr);
  798. node->executable = attr.executable;
  799. node->attachedMethod = method;
  800. node->methodHandle = handle;
  801. /* Add the node */
  802. UA_NodeId newMethodId;
  803. UA_NodeId_init(&newMethodId);
  804. UA_RCU_LOCK();
  805. UA_StatusCode retval = Service_AddNodes_existing(server, &adminSession, (UA_Node*)node, &parentNodeId,
  806. &referenceTypeId, &UA_NODEID_NULL, NULL, &newMethodId);
  807. UA_RCU_UNLOCK();
  808. if(retval != UA_STATUSCODE_GOOD)
  809. return retval;
  810. const UA_NodeId hasproperty = UA_NODEID_NUMERIC(0, UA_NS0ID_HASPROPERTY);
  811. const UA_NodeId propertytype = UA_NODEID_NUMERIC(0, UA_NS0ID_PROPERTYTYPE);
  812. if(inputArgumentsSize > 0) {
  813. UA_VariableNode *inputArgumentsVariableNode = UA_NodeStore_newVariableNode();
  814. inputArgumentsVariableNode->nodeId.namespaceIndex = newMethodId.namespaceIndex;
  815. inputArgumentsVariableNode->browseName = UA_QUALIFIEDNAME_ALLOC(0, "InputArguments");
  816. inputArgumentsVariableNode->displayName = UA_LOCALIZEDTEXT_ALLOC("en_US", "InputArguments");
  817. inputArgumentsVariableNode->description = UA_LOCALIZEDTEXT_ALLOC("en_US", "InputArguments");
  818. inputArgumentsVariableNode->valueRank = 1;
  819. /* UAExport creates a monitoreditem on inputarguments ... */
  820. inputArgumentsVariableNode->minimumSamplingInterval = 10000.0f;
  821. //TODO: 0.3 work item: the addMethodNode API does not have the possibility to set nodeIDs
  822. //actually we need to change the signature to pass UA_NS0ID_SERVER_GETMONITOREDITEMS_INPUTARGUMENTS
  823. //and UA_NS0ID_SERVER_GETMONITOREDITEMS_OUTPUTARGUMENTS into the function :/
  824. if(newMethodId.namespaceIndex == 0 &&
  825. newMethodId.identifierType == UA_NODEIDTYPE_NUMERIC &&
  826. newMethodId.identifier.numeric == UA_NS0ID_SERVER_GETMONITOREDITEMS) {
  827. inputArgumentsVariableNode->nodeId =
  828. UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_GETMONITOREDITEMS_INPUTARGUMENTS);
  829. }
  830. UA_Variant_setArrayCopy(&inputArgumentsVariableNode->value.data.value.value,
  831. inputArguments, inputArgumentsSize,
  832. &UA_TYPES[UA_TYPES_ARGUMENT]);
  833. inputArgumentsVariableNode->value.data.value.hasValue = true;
  834. UA_RCU_LOCK();
  835. // todo: check if adding succeeded
  836. Service_AddNodes_existing(server, &adminSession, (UA_Node*)inputArgumentsVariableNode,
  837. &newMethodId, &hasproperty, &propertytype, NULL, NULL);
  838. UA_RCU_UNLOCK();
  839. }
  840. if(outputArgumentsSize > 0) {
  841. /* create OutputArguments */
  842. UA_VariableNode *outputArgumentsVariableNode = UA_NodeStore_newVariableNode();
  843. outputArgumentsVariableNode->nodeId.namespaceIndex = newMethodId.namespaceIndex;
  844. outputArgumentsVariableNode->browseName = UA_QUALIFIEDNAME_ALLOC(0, "OutputArguments");
  845. outputArgumentsVariableNode->displayName = UA_LOCALIZEDTEXT_ALLOC("en_US", "OutputArguments");
  846. outputArgumentsVariableNode->description = UA_LOCALIZEDTEXT_ALLOC("en_US", "OutputArguments");
  847. outputArgumentsVariableNode->valueRank = 1;
  848. //FIXME: comment in line 882
  849. if(newMethodId.namespaceIndex == 0 &&
  850. newMethodId.identifierType == UA_NODEIDTYPE_NUMERIC &&
  851. newMethodId.identifier.numeric == UA_NS0ID_SERVER_GETMONITOREDITEMS) {
  852. outputArgumentsVariableNode->nodeId =
  853. UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_GETMONITOREDITEMS_OUTPUTARGUMENTS);
  854. }
  855. UA_Variant_setArrayCopy(&outputArgumentsVariableNode->value.data.value.value,
  856. outputArguments, outputArgumentsSize,
  857. &UA_TYPES[UA_TYPES_ARGUMENT]);
  858. outputArgumentsVariableNode->value.data.value.hasValue = true;
  859. UA_RCU_LOCK();
  860. // todo: check if adding succeeded
  861. Service_AddNodes_existing(server, &adminSession, (UA_Node*)outputArgumentsVariableNode,
  862. &newMethodId, &hasproperty, &propertytype, NULL, NULL);
  863. UA_RCU_UNLOCK();
  864. }
  865. if(outNewNodeId)
  866. *outNewNodeId = newMethodId;
  867. else
  868. UA_NodeId_deleteMembers(&newMethodId);
  869. return retval;
  870. }
  871. #endif
  872. /******************/
  873. /* Add References */
  874. /******************/
  875. static UA_StatusCode
  876. deleteOneWayReference(UA_Server *server, UA_Session *session, UA_Node *node,
  877. const UA_DeleteReferencesItem *item);
  878. /* Adds a one-way reference to the local nodestore */
  879. static UA_StatusCode
  880. addOneWayReference(UA_Server *server, UA_Session *session,
  881. UA_Node *node, const UA_AddReferencesItem *item) {
  882. size_t i = node->referencesSize;
  883. size_t refssize = (i+1) | 3; // so the realloc is not necessary every time
  884. UA_ReferenceNode *new_refs = (UA_ReferenceNode *)UA_realloc(node->references, sizeof(UA_ReferenceNode) * refssize);
  885. if(!new_refs)
  886. return UA_STATUSCODE_BADOUTOFMEMORY;
  887. node->references = new_refs;
  888. UA_ReferenceNode_init(&new_refs[i]);
  889. UA_StatusCode retval = UA_NodeId_copy(&item->referenceTypeId, &new_refs[i].referenceTypeId);
  890. retval |= UA_ExpandedNodeId_copy(&item->targetNodeId, &new_refs[i].targetId);
  891. new_refs[i].isInverse = !item->isForward;
  892. if(retval == UA_STATUSCODE_GOOD)
  893. node->referencesSize = i+1;
  894. else
  895. UA_ReferenceNode_deleteMembers(&new_refs[i]);
  896. return retval;
  897. }
  898. UA_StatusCode
  899. Service_AddReferences_single(UA_Server *server, UA_Session *session,
  900. const UA_AddReferencesItem *item) {
  901. /* Currently no expandednodeids are allowed */
  902. if(item->targetServerUri.length > 0)
  903. return UA_STATUSCODE_BADNOTIMPLEMENTED;
  904. /* Add the first direction */
  905. #ifndef UA_ENABLE_EXTERNAL_NAMESPACES
  906. UA_StatusCode retval = UA_Server_editNode(server, session, &item->sourceNodeId,
  907. (UA_EditNodeCallback)addOneWayReference,
  908. item);
  909. #else
  910. UA_StatusCode retval = UA_STATUSCODE_GOOD;
  911. UA_Boolean handledExternally = UA_FALSE;
  912. for(size_t j = 0; j <server->externalNamespacesSize; ++j) {
  913. if(item->sourceNodeId.namespaceIndex != server->externalNamespaces[j].index) {
  914. continue;
  915. } else {
  916. UA_ExternalNodeStore *ens = &server->externalNamespaces[j].externalNodeStore;
  917. retval = ens->addOneWayReference(ens->ensHandle, item);
  918. handledExternally = UA_TRUE;
  919. break;
  920. }
  921. }
  922. if(!handledExternally)
  923. retval = UA_Server_editNode(server, session, &item->sourceNodeId,
  924. (UA_EditNodeCallback)addOneWayReference, item);
  925. #endif
  926. if(retval != UA_STATUSCODE_GOOD)
  927. return retval;
  928. /* Add the second direction */
  929. UA_AddReferencesItem secondItem;
  930. UA_AddReferencesItem_init(&secondItem);
  931. secondItem.sourceNodeId = item->targetNodeId.nodeId;
  932. secondItem.referenceTypeId = item->referenceTypeId;
  933. secondItem.isForward = !item->isForward;
  934. secondItem.targetNodeId.nodeId = item->sourceNodeId;
  935. /* keep default secondItem.targetNodeClass = UA_NODECLASS_UNSPECIFIED */
  936. #ifndef UA_ENABLE_EXTERNAL_NAMESPACES
  937. retval = UA_Server_editNode(server, session, &secondItem.sourceNodeId,
  938. (UA_EditNodeCallback)addOneWayReference, &secondItem);
  939. #else
  940. handledExternally = UA_FALSE;
  941. for(size_t j = 0; j < server->externalNamespacesSize; ++j) {
  942. if(secondItem.sourceNodeId.namespaceIndex != server->externalNamespaces[j].index) {
  943. continue;
  944. } else {
  945. UA_ExternalNodeStore *ens = &server->externalNamespaces[j].externalNodeStore;
  946. retval = (UA_StatusCode)ens->addOneWayReference(ens->ensHandle, &secondItem);
  947. handledExternally = UA_TRUE;
  948. break;
  949. }
  950. }
  951. if(!handledExternally)
  952. retval = UA_Server_editNode(server, session, &secondItem.sourceNodeId,
  953. (UA_EditNodeCallback)addOneWayReference, &secondItem);
  954. #endif
  955. /* remove reference if the second direction failed */
  956. if(retval != UA_STATUSCODE_GOOD) {
  957. UA_DeleteReferencesItem deleteItem;
  958. deleteItem.sourceNodeId = item->sourceNodeId;
  959. deleteItem.referenceTypeId = item->referenceTypeId;
  960. deleteItem.isForward = item->isForward;
  961. deleteItem.targetNodeId = item->targetNodeId;
  962. deleteItem.deleteBidirectional = false;
  963. /* ignore returned status code */
  964. UA_Server_editNode(server, session, &item->sourceNodeId,
  965. (UA_EditNodeCallback)deleteOneWayReference, &deleteItem);
  966. }
  967. return retval;
  968. }
  969. void Service_AddReferences(UA_Server *server, UA_Session *session,
  970. const UA_AddReferencesRequest *request,
  971. UA_AddReferencesResponse *response) {
  972. UA_LOG_DEBUG_SESSION(server->config.logger, session,
  973. "Processing AddReferencesRequest");
  974. if(request->referencesToAddSize <= 0) {
  975. response->responseHeader.serviceResult = UA_STATUSCODE_BADNOTHINGTODO;
  976. return;
  977. }
  978. response->results = (UA_StatusCode *)UA_malloc(sizeof(UA_StatusCode) * request->referencesToAddSize);
  979. if(!response->results) {
  980. response->responseHeader.serviceResult = UA_STATUSCODE_BADOUTOFMEMORY;
  981. return;
  982. }
  983. response->resultsSize = request->referencesToAddSize;
  984. #ifndef UA_ENABLE_EXTERNAL_NAMESPACES
  985. for(size_t i = 0; i < response->resultsSize; ++i)
  986. response->results[i] =
  987. Service_AddReferences_single(server, session, &request->referencesToAdd[i]);
  988. #else
  989. size_t size = request->referencesToAddSize;
  990. # ifdef NO_ALLOCA
  991. UA_Boolean isExternal[size];
  992. UA_UInt32 indices[size];
  993. # else
  994. UA_Boolean *isExternal = UA_alloca(sizeof(UA_Boolean) * size);
  995. UA_UInt32 *indices = UA_alloca(sizeof(UA_UInt32) * size);
  996. # endif /*NO_ALLOCA */
  997. memset(isExternal, false, sizeof(UA_Boolean) * size);
  998. for(size_t j = 0; j < server->externalNamespacesSize; ++j) {
  999. size_t indicesSize = 0;
  1000. for(size_t i = 0;i < size;++i) {
  1001. if(request->referencesToAdd[i].sourceNodeId.namespaceIndex
  1002. != server->externalNamespaces[j].index)
  1003. continue;
  1004. isExternal[i] = true;
  1005. indices[indicesSize] = (UA_UInt32)i;
  1006. ++indicesSize;
  1007. }
  1008. if (indicesSize == 0)
  1009. continue;
  1010. UA_ExternalNodeStore *ens = &server->externalNamespaces[j].externalNodeStore;
  1011. ens->addReferences(ens->ensHandle, &request->requestHeader, request->referencesToAdd,
  1012. indices, (UA_UInt32)indicesSize, response->results, response->diagnosticInfos);
  1013. }
  1014. for(size_t i = 0; i < response->resultsSize; ++i) {
  1015. if(!isExternal[i])
  1016. response->results[i] =
  1017. Service_AddReferences_single(server, session, &request->referencesToAdd[i]);
  1018. }
  1019. #endif
  1020. }
  1021. UA_StatusCode
  1022. UA_Server_addReference(UA_Server *server, const UA_NodeId sourceId,
  1023. const UA_NodeId refTypeId, const UA_ExpandedNodeId targetId,
  1024. UA_Boolean isForward) {
  1025. UA_AddReferencesItem item;
  1026. UA_AddReferencesItem_init(&item);
  1027. item.sourceNodeId = sourceId;
  1028. item.referenceTypeId = refTypeId;
  1029. item.isForward = isForward;
  1030. item.targetNodeId = targetId;
  1031. UA_RCU_LOCK();
  1032. UA_StatusCode retval = Service_AddReferences_single(server, &adminSession, &item);
  1033. UA_RCU_UNLOCK();
  1034. return retval;
  1035. }
  1036. /****************/
  1037. /* Delete Nodes */
  1038. /****************/
  1039. static void
  1040. removeReferences(UA_Server *server, UA_Session *session, const UA_Node *node) {
  1041. UA_DeleteReferencesItem item;
  1042. UA_DeleteReferencesItem_init(&item);
  1043. item.targetNodeId.nodeId = node->nodeId;
  1044. for(size_t i = 0; i < node->referencesSize; ++i) {
  1045. item.isForward = node->references[i].isInverse;
  1046. item.sourceNodeId = node->references[i].targetId.nodeId;
  1047. item.referenceTypeId = node->references[i].referenceTypeId;
  1048. Service_DeleteReferences_single(server, session, &item);
  1049. }
  1050. }
  1051. UA_StatusCode
  1052. Service_DeleteNodes_single(UA_Server *server, UA_Session *session,
  1053. const UA_NodeId *nodeId, UA_Boolean deleteReferences) {
  1054. const UA_Node *node = UA_NodeStore_get(server->nodestore, nodeId);
  1055. if(!node)
  1056. return UA_STATUSCODE_BADNODEIDUNKNOWN;
  1057. /* TODO: check if the information model consistency is violated */
  1058. /* Destroy an object before removing it */
  1059. if(node->nodeClass == UA_NODECLASS_OBJECT) {
  1060. /* Call the destructor from the object type */
  1061. const UA_ObjectTypeNode *typenode = getObjectNodeType(server, (const UA_ObjectNode*)node);
  1062. if(typenode && typenode->lifecycleManagement.destructor)
  1063. typenode->lifecycleManagement.destructor(*nodeId, ((const UA_ObjectNode*)node)->instanceHandle);
  1064. }
  1065. /* Remove references to the node (not the references in the node that will
  1066. * be deleted anyway) */
  1067. if(deleteReferences)
  1068. removeReferences(server, session, node);
  1069. return UA_NodeStore_remove(server->nodestore, nodeId);
  1070. }
  1071. void Service_DeleteNodes(UA_Server *server, UA_Session *session,
  1072. const UA_DeleteNodesRequest *request,
  1073. UA_DeleteNodesResponse *response) {
  1074. UA_LOG_DEBUG_SESSION(server->config.logger, session,
  1075. "Processing DeleteNodesRequest");
  1076. if(request->nodesToDeleteSize == 0) {
  1077. response->responseHeader.serviceResult = UA_STATUSCODE_BADNOTHINGTODO;
  1078. return;
  1079. }
  1080. response->results = (UA_StatusCode *)UA_malloc(sizeof(UA_StatusCode) * request->nodesToDeleteSize);
  1081. if(!response->results) {
  1082. response->responseHeader.serviceResult = UA_STATUSCODE_BADOUTOFMEMORY;;
  1083. return;
  1084. }
  1085. response->resultsSize = request->nodesToDeleteSize;
  1086. for(size_t i = 0; i < request->nodesToDeleteSize; ++i) {
  1087. UA_DeleteNodesItem *item = &request->nodesToDelete[i];
  1088. response->results[i] = Service_DeleteNodes_single(server, session, &item->nodeId,
  1089. item->deleteTargetReferences);
  1090. }
  1091. }
  1092. UA_StatusCode
  1093. UA_Server_deleteNode(UA_Server *server, const UA_NodeId nodeId,
  1094. UA_Boolean deleteReferences) {
  1095. UA_RCU_LOCK();
  1096. UA_StatusCode retval = Service_DeleteNodes_single(server, &adminSession,
  1097. &nodeId, deleteReferences);
  1098. UA_RCU_UNLOCK();
  1099. return retval;
  1100. }
  1101. /*********************/
  1102. /* Delete References */
  1103. /*********************/
  1104. // TODO: Check consistency constraints, remove the references.
  1105. static UA_StatusCode
  1106. deleteOneWayReference(UA_Server *server, UA_Session *session, UA_Node *node,
  1107. const UA_DeleteReferencesItem *item) {
  1108. UA_Boolean edited = false;
  1109. for(size_t i = node->referencesSize; i > 0; --i) {
  1110. UA_ReferenceNode *ref = &node->references[i-1];
  1111. if(!UA_NodeId_equal(&item->targetNodeId.nodeId, &ref->targetId.nodeId))
  1112. continue;
  1113. if(!UA_NodeId_equal(&item->referenceTypeId, &ref->referenceTypeId))
  1114. continue;
  1115. if(item->isForward == ref->isInverse)
  1116. continue;
  1117. UA_ReferenceNode_deleteMembers(ref);
  1118. /* move the last entry to override the current position */
  1119. node->references[i-1] = node->references[node->referencesSize-1];
  1120. --node->referencesSize;
  1121. edited = true;
  1122. break;
  1123. }
  1124. if(!edited)
  1125. return UA_STATUSCODE_UNCERTAINREFERENCENOTDELETED;
  1126. /* we removed the last reference */
  1127. if(node->referencesSize == 0 && node->references) {
  1128. UA_free(node->references);
  1129. node->references = NULL;
  1130. }
  1131. return UA_STATUSCODE_GOOD;;
  1132. }
  1133. UA_StatusCode
  1134. Service_DeleteReferences_single(UA_Server *server, UA_Session *session,
  1135. const UA_DeleteReferencesItem *item) {
  1136. UA_StatusCode retval = UA_Server_editNode(server, session, &item->sourceNodeId,
  1137. (UA_EditNodeCallback)deleteOneWayReference, item);
  1138. if(retval != UA_STATUSCODE_GOOD)
  1139. return retval;
  1140. if(!item->deleteBidirectional || item->targetNodeId.serverIndex != 0)
  1141. return retval;
  1142. UA_DeleteReferencesItem secondItem;
  1143. UA_DeleteReferencesItem_init(&secondItem);
  1144. secondItem.isForward = !item->isForward;
  1145. secondItem.sourceNodeId = item->targetNodeId.nodeId;
  1146. secondItem.targetNodeId.nodeId = item->sourceNodeId;
  1147. secondItem.referenceTypeId = item->referenceTypeId;
  1148. return UA_Server_editNode(server, session, &secondItem.sourceNodeId,
  1149. (UA_EditNodeCallback)deleteOneWayReference, &secondItem);
  1150. }
  1151. void
  1152. Service_DeleteReferences(UA_Server *server, UA_Session *session,
  1153. const UA_DeleteReferencesRequest *request,
  1154. UA_DeleteReferencesResponse *response) {
  1155. UA_LOG_DEBUG_SESSION(server->config.logger, session,
  1156. "Processing DeleteReferencesRequest");
  1157. if(request->referencesToDeleteSize <= 0) {
  1158. response->responseHeader.serviceResult = UA_STATUSCODE_BADNOTHINGTODO;
  1159. return;
  1160. }
  1161. response->results = (UA_StatusCode *)UA_malloc(sizeof(UA_StatusCode) * request->referencesToDeleteSize);
  1162. if(!response->results) {
  1163. response->responseHeader.serviceResult = UA_STATUSCODE_BADOUTOFMEMORY;;
  1164. return;
  1165. }
  1166. response->resultsSize = request->referencesToDeleteSize;
  1167. for(size_t i = 0; i < request->referencesToDeleteSize; ++i)
  1168. response->results[i] =
  1169. Service_DeleteReferences_single(server, session, &request->referencesToDelete[i]);
  1170. }
  1171. UA_StatusCode
  1172. UA_Server_deleteReference(UA_Server *server, const UA_NodeId sourceNodeId,
  1173. const UA_NodeId referenceTypeId,
  1174. UA_Boolean isForward, const UA_ExpandedNodeId targetNodeId,
  1175. UA_Boolean deleteBidirectional) {
  1176. UA_DeleteReferencesItem item;
  1177. item.sourceNodeId = sourceNodeId;
  1178. item.referenceTypeId = referenceTypeId;
  1179. item.isForward = isForward;
  1180. item.targetNodeId = targetNodeId;
  1181. item.deleteBidirectional = deleteBidirectional;
  1182. UA_RCU_LOCK();
  1183. UA_StatusCode retval = Service_DeleteReferences_single(server, &adminSession, &item);
  1184. UA_RCU_UNLOCK();
  1185. return retval;
  1186. }
  1187. /**********************/
  1188. /* Set Value Callback */
  1189. /**********************/
  1190. static UA_StatusCode
  1191. setValueCallback(UA_Server *server, UA_Session *session,
  1192. UA_VariableNode *node, UA_ValueCallback *callback) {
  1193. if(node->nodeClass != UA_NODECLASS_VARIABLE)
  1194. return UA_STATUSCODE_BADNODECLASSINVALID;
  1195. node->value.data.callback = *callback;
  1196. return UA_STATUSCODE_GOOD;
  1197. }
  1198. UA_StatusCode UA_EXPORT
  1199. UA_Server_setVariableNode_valueCallback(UA_Server *server, const UA_NodeId nodeId,
  1200. const UA_ValueCallback callback) {
  1201. UA_RCU_LOCK();
  1202. UA_StatusCode retval = UA_Server_editNode(server, &adminSession, &nodeId,
  1203. (UA_EditNodeCallback)setValueCallback, &callback);
  1204. UA_RCU_UNLOCK();
  1205. return retval;
  1206. }
  1207. /******************/
  1208. /* Set DataSource */
  1209. /******************/
  1210. static UA_StatusCode
  1211. setDataSource(UA_Server *server, UA_Session *session,
  1212. UA_VariableNode* node, UA_DataSource *dataSource) {
  1213. if(node->nodeClass != UA_NODECLASS_VARIABLE)
  1214. return UA_STATUSCODE_BADNODECLASSINVALID;
  1215. if(node->valueSource == UA_VALUESOURCE_DATA)
  1216. UA_DataValue_deleteMembers(&node->value.data.value);
  1217. node->value.dataSource = *dataSource;
  1218. node->valueSource = UA_VALUESOURCE_DATASOURCE;
  1219. return UA_STATUSCODE_GOOD;
  1220. }
  1221. UA_StatusCode
  1222. UA_Server_setVariableNode_dataSource(UA_Server *server, const UA_NodeId nodeId,
  1223. const UA_DataSource dataSource) {
  1224. UA_RCU_LOCK();
  1225. UA_StatusCode retval = UA_Server_editNode(server, &adminSession, &nodeId,
  1226. (UA_EditNodeCallback)setDataSource, &dataSource);
  1227. UA_RCU_UNLOCK();
  1228. return retval;
  1229. }
  1230. /****************************/
  1231. /* Set Lifecycle Management */
  1232. /****************************/
  1233. static UA_StatusCode
  1234. setOLM(UA_Server *server, UA_Session *session,
  1235. UA_ObjectTypeNode* node, UA_ObjectLifecycleManagement *olm) {
  1236. if(node->nodeClass != UA_NODECLASS_OBJECTTYPE)
  1237. return UA_STATUSCODE_BADNODECLASSINVALID;
  1238. node->lifecycleManagement = *olm;
  1239. return UA_STATUSCODE_GOOD;
  1240. }
  1241. UA_StatusCode UA_EXPORT
  1242. UA_Server_setObjectTypeNode_lifecycleManagement(UA_Server *server, UA_NodeId nodeId,
  1243. UA_ObjectLifecycleManagement olm) {
  1244. UA_RCU_LOCK();
  1245. UA_StatusCode retval = UA_Server_editNode(server, &adminSession, &nodeId,
  1246. (UA_EditNodeCallback)setOLM, &olm);
  1247. UA_RCU_UNLOCK();
  1248. return retval;
  1249. }
  1250. /***********************/
  1251. /* Set Method Callback */
  1252. /***********************/
  1253. #ifdef UA_ENABLE_METHODCALLS
  1254. struct addMethodCallback {
  1255. UA_MethodCallback callback;
  1256. void *handle;
  1257. };
  1258. static UA_StatusCode
  1259. editMethodCallback(UA_Server *server, UA_Session* session,
  1260. UA_Node* node, const void* handle) {
  1261. if(node->nodeClass != UA_NODECLASS_METHOD)
  1262. return UA_STATUSCODE_BADNODECLASSINVALID;
  1263. const struct addMethodCallback *newCallback = (const struct addMethodCallback *)handle;
  1264. UA_MethodNode *mnode = (UA_MethodNode*) node;
  1265. mnode->attachedMethod = newCallback->callback;
  1266. mnode->methodHandle = newCallback->handle;
  1267. return UA_STATUSCODE_GOOD;
  1268. }
  1269. UA_StatusCode UA_EXPORT
  1270. UA_Server_setMethodNode_callback(UA_Server *server, const UA_NodeId methodNodeId,
  1271. UA_MethodCallback method, void *handle) {
  1272. struct addMethodCallback cb = { method, handle };
  1273. UA_RCU_LOCK();
  1274. UA_StatusCode retval = UA_Server_editNode(server, &adminSession,
  1275. &methodNodeId, editMethodCallback, &cb);
  1276. UA_RCU_UNLOCK();
  1277. return retval;
  1278. }
  1279. #endif