Line data Source code
1 : /*
2 : *
3 : *
4 : * Distributed under the OpenDDS License.
5 : * See: http://www.opendds.org/license.html
6 : */
7 :
8 : #include "DCPS/DdsDcps_pch.h"
9 : #include "DCPS_Utils.h"
10 :
11 : #include "Qos_Helper.h"
12 : #include "Definitions.h"
13 : #include "SafetyProfileStreams.h"
14 :
15 : #include <ace/ACE.h> /* For ACE::wild_match() */
16 : #include <ace/OS_NS_string.h>
17 :
18 : #ifdef OPENDDS_SECURITY
19 : # include "dds/DdsSecurityCoreC.h"
20 : #endif
21 :
22 : #include <cstring>
23 :
24 : OPENDDS_BEGIN_VERSIONED_NAMESPACE_DECL
25 :
26 : namespace OpenDDS {
27 : namespace DCPS {
28 :
29 8 : const char* retcode_to_string(DDS::ReturnCode_t value)
30 : {
31 8 : switch (value) {
32 0 : case DDS::RETCODE_OK:
33 0 : return "OK";
34 8 : case DDS::RETCODE_ERROR:
35 8 : return "Error";
36 0 : case DDS::RETCODE_UNSUPPORTED:
37 0 : return "Unsupported";
38 0 : case DDS::RETCODE_BAD_PARAMETER:
39 0 : return "Bad parameter";
40 0 : case DDS::RETCODE_PRECONDITION_NOT_MET:
41 0 : return "Precondition not met";
42 0 : case DDS::RETCODE_OUT_OF_RESOURCES:
43 0 : return "Out of resources";
44 0 : case DDS::RETCODE_NOT_ENABLED:
45 0 : return "Not enabled";
46 0 : case DDS::RETCODE_IMMUTABLE_POLICY:
47 0 : return "Immutable policy";
48 0 : case DDS::RETCODE_INCONSISTENT_POLICY:
49 0 : return "Inconsistent policy";
50 0 : case DDS::RETCODE_ALREADY_DELETED:
51 0 : return "Already deleted";
52 0 : case DDS::RETCODE_TIMEOUT:
53 0 : return "Timeout";
54 0 : case DDS::RETCODE_NO_DATA:
55 0 : return "No data";
56 0 : case DDS::RETCODE_ILLEGAL_OPERATION:
57 0 : return "Illegal operation";
58 : #ifdef OPENDDS_SECURITY
59 0 : case DDS::Security::RETCODE_NOT_ALLOWED_BY_SECURITY:
60 0 : return "Not allowed by security";
61 : #endif
62 0 : default:
63 0 : ACE_ERROR((LM_ERROR, ACE_TEXT("(%P|%t) ERROR: retcode_to_string: ")
64 : ACE_TEXT("%d is either invalid or not recognized.\n"),
65 : value));
66 0 : return "Invalid return code";
67 : }
68 : }
69 :
70 0 : const char* topicstatus_to_string(TopicStatus value)
71 : {
72 0 : switch (value) {
73 0 : case CREATED:
74 0 : return "Created";
75 0 : case ENABLED:
76 0 : return "Enabled";
77 0 : case FOUND:
78 0 : return "Found";
79 0 : case NOT_FOUND:
80 0 : return "Not found";
81 0 : case REMOVED:
82 0 : return "Removed";
83 0 : case CONFLICTING_TYPENAME:
84 0 : return "Conflicting typename";
85 0 : case PRECONDITION_NOT_MET:
86 0 : return "Precondition not met";
87 0 : case INTERNAL_ERROR:
88 0 : return "Internal error";
89 0 : case TOPIC_DISABLED:
90 0 : return "Topic disabled";
91 0 : default:
92 0 : ACE_ERROR((LM_ERROR, ACE_TEXT("(%P|%t) ERROR: topicstatus_to_string: ")
93 : ACE_TEXT("%d is either invalid or not recognized.\n"),
94 : value));
95 0 : return "Invalid topic status";
96 : }
97 : }
98 :
99 : bool
100 0 : is_wildcard(const char *str)
101 : {
102 : static const char wild[] = "?*[";
103 :
104 0 : while (*str) {
105 0 : size_t i = ACE_OS::strcspn(str, wild);
106 :
107 0 : if (!str[i]) return false; // no wildcard
108 :
109 0 : if (i > 0 && str[i-1] == '\\') str += i + 1; // escaped wildcard
110 :
111 0 : else return true;
112 : }
113 :
114 0 : return false;
115 : }
116 :
117 : class PartitionName {
118 : public:
119 0 : PartitionName(const char* name)
120 0 : : name_(name),
121 0 : wildcard_(is_wildcard(name)) {}
122 :
123 0 : bool matches(const PartitionName& n) {
124 0 : if (wildcard_ && n.wildcard_)
125 0 : return false; // wildcards never match
126 :
127 0 : if (wildcard_)
128 0 : return ACE::wild_match(n.name_, name_, true, true);
129 :
130 0 : else if (n.wildcard_)
131 0 : return ACE::wild_match(name_, n.name_, true, true);
132 :
133 : else
134 0 : return ACE_OS::strcmp(name_, n.name_) == 0;
135 : }
136 :
137 : private:
138 : const char* name_;
139 : bool wildcard_;
140 : };
141 :
142 : bool
143 0 : matches_name(const DDS::PartitionQosPolicy& qos, const PartitionName& name)
144 : {
145 0 : for (CORBA::ULong i = 0; i < qos.name.length(); ++i) {
146 0 : PartitionName qos_name(qos.name[i]);
147 :
148 0 : if (qos_name.matches(name))
149 0 : return true;
150 : }
151 :
152 0 : return false;
153 : }
154 :
155 : bool
156 0 : matches_default(const DDS::PartitionQosPolicy& qos)
157 : {
158 0 : if (qos.name.length() == 0)
159 0 : return true; // default
160 :
161 0 : for (CORBA::ULong i = 0; i < qos.name.length(); ++i) {
162 0 : if (*qos.name[i] == 0)
163 0 : return true; // default (empty string)
164 : }
165 :
166 0 : return false;
167 : }
168 :
169 : bool
170 0 : matching_partitions(const DDS::PartitionQosPolicy& pub,
171 : const DDS::PartitionQosPolicy& sub)
172 : {
173 0 : if (matches_default(pub)) {
174 0 : if (matches_default(sub))
175 0 : return true;
176 :
177 : // Zero-length sequences should be treated the same as a
178 : // sequence of length 1 that contains an empty string:
179 0 : if (pub.name.length() == 0)
180 0 : return matches_name(sub, "");
181 : }
182 :
183 0 : for (CORBA::ULong i = 0; i < pub.name.length(); ++i) {
184 0 : const char* name = pub.name[i];
185 :
186 0 : if (matches_name(sub, name))
187 0 : return true;
188 : }
189 :
190 0 : return false;
191 : }
192 :
193 : void
194 0 : increment_incompatibility_count(OpenDDS::DCPS::IncompatibleQosStatus* status,
195 : DDS::QosPolicyId_t incompatible_policy)
196 : {
197 0 : if (!status) return;
198 :
199 0 : ++status->total_count;
200 0 : ++status->count_since_last_send;
201 0 : status->last_policy_id = incompatible_policy;
202 0 : CORBA::ULong const size = status->policies.length();
203 0 : CORBA::ULong count = 0;
204 0 : bool updated = false;
205 :
206 0 : for (; !updated && count < size; ++count) {
207 0 : if (status->policies[count].policy_id == incompatible_policy) {
208 0 : ++status->policies[count].count;
209 0 : updated = true;
210 : }
211 : }
212 :
213 0 : if (!updated) {
214 : DDS::QosPolicyCount policy;
215 0 : policy.policy_id = incompatible_policy;
216 0 : policy.count = 1;
217 0 : status->policies.length(count + 1);
218 0 : status->policies[count] = policy;
219 : }
220 : }
221 :
222 : bool
223 0 : compatibleTransports(const OpenDDS::DCPS::TransportLocatorSeq& s1,
224 : const OpenDDS::DCPS::TransportLocatorSeq& s2)
225 : {
226 0 : for (CORBA::ULong i = 0; i < s1.length(); ++i) {
227 0 : for (CORBA::ULong j = 0; j < s2.length(); ++j) {
228 0 : if (0 == std::strcmp(s1[i].transport_type, s2[j].transport_type)) {
229 0 : return true;
230 : }
231 : }
232 : }
233 0 : return false;
234 : }
235 :
236 : bool
237 0 : compatibleQOS(OpenDDS::DCPS::IncompatibleQosStatus* writerStatus,
238 : OpenDDS::DCPS::IncompatibleQosStatus* readerStatus,
239 : const OpenDDS::DCPS::TransportLocatorSeq& pubTLS,
240 : const OpenDDS::DCPS::TransportLocatorSeq& subTLS,
241 : DDS::DataWriterQos const * const writerQos,
242 : DDS::DataReaderQos const * const readerQos,
243 : DDS::PublisherQos const * const pubQos,
244 : DDS::SubscriberQos const * const subQos)
245 : {
246 0 : bool compatible = true;
247 :
248 : // Check transport-type compatibility
249 0 : if (!compatibleTransports(pubTLS, subTLS)) {
250 0 : compatible = false;
251 0 : increment_incompatibility_count(writerStatus,
252 : OpenDDS::TRANSPORTTYPE_QOS_POLICY_ID);
253 0 : increment_incompatibility_count(readerStatus,
254 : OpenDDS::TRANSPORTTYPE_QOS_POLICY_ID);
255 : }
256 :
257 : // Verify compatibility of DataWriterQos and DataReaderQos
258 0 : compatible = compatible && compatibleQOS(writerQos, readerQos,
259 : writerStatus, readerStatus);
260 :
261 : // Verify compatibility of PublisherQos and SubscriberQos
262 0 : compatible = compatible && compatibleQOS(pubQos, subQos,
263 : writerStatus, readerStatus);
264 :
265 : // Verify publisher and subscriber are in a matching partition.
266 : //
267 : // According to the DDS spec:
268 : //
269 : // Failure to match partitions is not considered an incompatible
270 : // QoS and does not trigger any listeners nor conditions.
271 : //
272 : // Don't increment the incompatibity count.
273 0 : compatible = compatible && matching_partitions(pubQos->partition,
274 0 : subQos->partition);
275 :
276 0 : return compatible;
277 : }
278 :
279 : bool
280 0 : compatibleQOS(const DDS::PublisherQos* pubQos,
281 : const DDS::SubscriberQos* subQos,
282 : OpenDDS::DCPS::IncompatibleQosStatus* writerStatus,
283 : OpenDDS::DCPS::IncompatibleQosStatus* readerStatus)
284 : {
285 0 : bool compatible = true;
286 :
287 : // PARTITION, GROUP_DATA, and ENTITY_FACTORY are RxO==no.
288 :
289 : // Check the PRESENTATION_QOS_POLICY_ID
290 0 : if ((pubQos->presentation.access_scope < subQos->presentation.access_scope)
291 0 : || (!pubQos->presentation.coherent_access && subQos->presentation.coherent_access)
292 0 : || (!pubQos->presentation.ordered_access && subQos->presentation.ordered_access)) {
293 0 : compatible = false;
294 :
295 0 : increment_incompatibility_count(writerStatus,
296 : DDS::PRESENTATION_QOS_POLICY_ID);
297 0 : increment_incompatibility_count(readerStatus,
298 : DDS::PRESENTATION_QOS_POLICY_ID);
299 : }
300 :
301 0 : return compatible;
302 : }
303 :
304 : bool
305 0 : compatibleQOS(const DDS::DataWriterQos * writerQos,
306 : const DDS::DataReaderQos * readerQos,
307 : OpenDDS::DCPS::IncompatibleQosStatus* writerStatus,
308 : OpenDDS::DCPS::IncompatibleQosStatus* readerStatus)
309 : {
310 0 : bool compatible = true;
311 :
312 : // Check the RELIABILITY_QOS_POLICY_ID
313 0 : if (writerQos->reliability.kind < readerQos->reliability.kind) {
314 0 : compatible = false;
315 :
316 0 : increment_incompatibility_count(writerStatus,
317 : DDS::RELIABILITY_QOS_POLICY_ID);
318 0 : increment_incompatibility_count(readerStatus,
319 : DDS::RELIABILITY_QOS_POLICY_ID);
320 : }
321 :
322 : // Check the DURABILITY_QOS_POLICY_ID
323 0 : if (writerQos->durability.kind < readerQos->durability.kind) {
324 0 : compatible = false;
325 :
326 0 : increment_incompatibility_count(writerStatus,
327 : DDS::DURABILITY_QOS_POLICY_ID);
328 0 : increment_incompatibility_count(readerStatus,
329 : DDS::DURABILITY_QOS_POLICY_ID);
330 : }
331 :
332 : // Check the LIVELINESS_QOS_POLICY_ID
333 : // invalid if offered kind is less than requested kind OR
334 : // if offered liveliness duration greater than requested
335 : // liveliness duration
336 : using OpenDDS::DCPS::operator>;
337 0 : if (writerQos->liveliness.kind < readerQos->liveliness.kind
338 0 : || writerQos->liveliness.lease_duration
339 0 : > readerQos->liveliness.lease_duration) {
340 :
341 0 : compatible = false;
342 :
343 0 : increment_incompatibility_count(writerStatus,
344 : DDS::LIVELINESS_QOS_POLICY_ID);
345 0 : increment_incompatibility_count(readerStatus,
346 : DDS::LIVELINESS_QOS_POLICY_ID);
347 : }
348 :
349 : // Check the DEADLINE_QOS_POLICY_ID
350 : // Offered deadline must be less than or equal to the requested
351 : // deadline.
352 0 : if (writerQos->deadline.period > readerQos->deadline.period) {
353 :
354 0 : compatible = false;
355 :
356 0 : increment_incompatibility_count(writerStatus,
357 : DDS::DEADLINE_QOS_POLICY_ID);
358 0 : increment_incompatibility_count(readerStatus,
359 : DDS::DEADLINE_QOS_POLICY_ID);
360 : }
361 :
362 : // Check the LATENCY_BUDGET
363 : // The reader's duration must be greater than or equal to the writer's
364 : using OpenDDS::DCPS::operator<;
365 0 : if (readerQos->latency_budget.duration < writerQos->latency_budget.duration) {
366 :
367 0 : compatible = false;
368 :
369 0 : increment_incompatibility_count(writerStatus,
370 : DDS::LATENCYBUDGET_QOS_POLICY_ID);
371 0 : increment_incompatibility_count(readerStatus,
372 : DDS::LATENCYBUDGET_QOS_POLICY_ID);
373 : }
374 :
375 : // The value of the OWNERSHIP kind offered must exactly match the one
376 : // requested or else they are considered incompatible.
377 0 : if (writerQos->ownership.kind != readerQos->ownership.kind) {
378 0 : compatible = false;
379 :
380 0 : increment_incompatibility_count(writerStatus,
381 : DDS::OWNERSHIP_QOS_POLICY_ID);
382 0 : increment_incompatibility_count(readerStatus,
383 : DDS::OWNERSHIP_QOS_POLICY_ID);
384 : }
385 :
386 : {
387 : // Find a common data representation
388 0 : bool found = false;
389 0 : const CORBA::ULong reader_count = readerQos->representation.value.length();
390 0 : const CORBA::ULong writer_count = writerQos->representation.value.length();
391 0 : for (CORBA::ULong wi = 0; !found && wi < writer_count; ++wi) {
392 0 : for (CORBA::ULong ri = 0; !found && ri < reader_count; ++ri) {
393 0 : if (readerQos->representation.value[ri] == writerQos->representation.value[wi]) {
394 0 : found = true;
395 0 : break;
396 : }
397 : }
398 : }
399 :
400 0 : if (!found) {
401 0 : increment_incompatibility_count(writerStatus,
402 : DDS::DATA_REPRESENTATION_QOS_POLICY_ID);
403 0 : increment_incompatibility_count(readerStatus,
404 : DDS::DATA_REPRESENTATION_QOS_POLICY_ID);
405 0 : compatible = false;
406 : }
407 : }
408 :
409 0 : return compatible;
410 : }
411 :
412 : #ifndef OPENDDS_SAFETY_PROFILE
413 : using OpenDDS::DCPS::operator==;
414 : #endif
415 0 : bool should_check_association_upon_change(const DDS::DataReaderQos & qos1,
416 : const DDS::DataReaderQos & qos2)
417 : {
418 0 : return !(
419 0 : (qos1.deadline == qos2.deadline) &&
420 0 : (qos1.latency_budget == qos2.latency_budget));
421 : }
422 :
423 0 : bool should_check_association_upon_change(const DDS::DataWriterQos & qos1,
424 : const DDS::DataWriterQos & qos2)
425 : {
426 0 : return !(
427 0 : (qos1.deadline == qos2.deadline) &&
428 0 : (qos1.latency_budget == qos2.latency_budget));
429 : }
430 :
431 0 : bool should_check_association_upon_change(const DDS::SubscriberQos & qos1,
432 : const DDS::SubscriberQos & qos2)
433 : {
434 0 : return !(qos1.partition == qos2.partition);
435 : }
436 :
437 0 : bool should_check_association_upon_change(const DDS::PublisherQos & qos1,
438 : const DDS::PublisherQos & qos2)
439 : {
440 0 : return !(qos1.partition == qos2.partition);
441 : }
442 :
443 0 : bool should_check_association_upon_change(const DDS::TopicQos & /*qos1*/,
444 : const DDS::TopicQos & /*qos2*/)
445 : {
446 0 : return false;
447 : }
448 :
449 0 : bool should_check_association_upon_change(const DDS::DomainParticipantQos & /*qos1*/,
450 : const DDS::DomainParticipantQos & /*qos2*/)
451 : {
452 0 : return false;
453 : }
454 :
455 0 : bool repr_to_encoding_kind(DDS::DataRepresentationId_t repr, Encoding::Kind& kind)
456 : {
457 0 : switch(repr) {
458 0 : case DDS::XCDR_DATA_REPRESENTATION:
459 0 : kind = Encoding::KIND_XCDR1;
460 0 : break;
461 0 : case DDS::XCDR2_DATA_REPRESENTATION:
462 0 : kind = Encoding::KIND_XCDR2;
463 0 : break;
464 0 : case OpenDDS::DCPS::UNALIGNED_CDR_DATA_REPRESENTATION:
465 0 : kind = Encoding::KIND_UNALIGNED_CDR;
466 0 : break;
467 0 : default:
468 0 : return false;
469 : }
470 0 : return true;
471 : }
472 :
473 0 : DCPS::String repr_to_string(const DDS::DataRepresentationId_t& repr)
474 : {
475 0 : DCPS::String repr_string;
476 0 : switch (repr) {
477 0 : case DDS::XCDR_DATA_REPRESENTATION:
478 0 : repr_string = "XCDR_DATA_REPRESENTATION";
479 0 : break;
480 0 : case DDS::XML_DATA_REPRESENTATION:
481 0 : repr_string = "XML_DATA_REPRESENTATION";
482 0 : break;
483 0 : case DDS::XCDR2_DATA_REPRESENTATION:
484 0 : repr_string = "XCDR2_DATA_REPRESENTATION";
485 0 : break;
486 0 : case OpenDDS::DCPS::UNALIGNED_CDR_DATA_REPRESENTATION:
487 0 : repr_string = "UNALIGNED_CDR_DATA_REPRESENTATION";
488 0 : break;
489 0 : default:
490 0 : repr_string = to_dds_string(repr);
491 : }
492 0 : return repr_string;
493 0 : }
494 :
495 0 : DCPS::String repr_seq_to_string(const DDS::DataRepresentationIdSeq& id_seq, bool is_data_writer)
496 : {
497 0 : DCPS::String repr_string;
498 0 : const ACE_CDR::ULong length = is_data_writer ? 1 : id_seq.length();
499 0 : for (ACE_CDR::ULong i = 0; i < length; ++i) {
500 0 : if (i > 0) {
501 0 : repr_string += ", ";
502 : }
503 0 : repr_string += repr_to_string(id_seq[i]);
504 : }
505 0 : return repr_string;
506 0 : }
507 :
508 0 : void set_writer_effective_data_rep_qos(DDS::DataRepresentationIdSeq& qos,
509 : bool cdr_encapsulated)
510 : {
511 0 : if (qos.length() == 0) {
512 0 : qos.length(1);
513 0 : qos[0] = cdr_encapsulated ? DDS::XCDR2_DATA_REPRESENTATION : OpenDDS::DCPS::UNALIGNED_CDR_DATA_REPRESENTATION;
514 : }
515 0 : }
516 :
517 81 : void set_reader_effective_data_rep_qos(DDS::DataRepresentationIdSeq& qos)
518 : {
519 81 : if (qos.length() == 0) {
520 81 : qos.length(3);
521 81 : qos[0] = DDS::XCDR2_DATA_REPRESENTATION;
522 81 : qos[1] = DDS::XCDR_DATA_REPRESENTATION;
523 81 : qos[2] = OpenDDS::DCPS::UNALIGNED_CDR_DATA_REPRESENTATION;
524 : }
525 81 : }
526 :
527 : } // namespace DCPS
528 : } // namespace OpenDDS
529 :
530 : OPENDDS_END_VERSIONED_NAMESPACE_DECL
|