1 #include <CoreServices/CoreServices.h>
3 #include <libkern/OSAtomic.h>
6 #include "StreamSource.h"
7 #include "SingleShotSource.h"
12 #include "SecTransformInternal.h"
13 #include "GroupTransform.h"
14 #include "GroupTransform.h"
17 static const int kMaxPendingTransactions
= 20;
19 static CFTypeID internalID
= _kCFRuntimeNotATypeID
;
21 // Use &dispatchQueueToTransformKey as a key to dispatch_get_specific to map from
22 // a transforms master, activation, or any attribute queue to the Transform*
23 static unsigned char dispatchQueueToTransformKey
;
25 static char RandomChar()
27 return arc4random() % 26 + 'A'; // good enough
31 static CFStringRef
ah_set_describe(const void *v
) CF_RETURNS_RETAINED
{
32 transform_attribute
*ta
= ah2ta(static_cast<SecTransformAttributeRef
>(const_cast<void*>(v
)));
33 return CFStringCreateWithFormat(NULL
, NULL
, CFSTR("%@/%@=%@ (conn: %@)"), ta
->transform
->GetName(), ta
->name
, ta
->value
? ta
->value
: CFSTR("NULL"), ta
->connections
? static_cast<CFTypeRef
>(ta
->connections
) : static_cast<CFTypeRef
>(CFSTR("NONE")));
36 static CFStringRef
AttributeHandleFormat(CFTypeRef ah
, CFDictionaryRef dict
) CF_RETURNS_RETAINED
{
37 transform_attribute
*ta
= ah2ta(ah
);
38 return CFStringCreateWithFormat(NULL
, NULL
, CFSTR("%@/%@"), ta
->transform
->GetName(), ta
->name
);
41 static CFStringRef
AttributeHandleDebugFormat(CFTypeRef ah
) CF_RETURNS_RETAINED
{
42 transform_attribute
*ta
= ah2ta(ah
);
43 return CFStringCreateWithFormat(NULL
, NULL
, CFSTR("%@/%@ (%p)"), ta
->transform
->GetName(), ta
->name
, ta
);
46 static void AttributeHandleFinalize(CFTypeRef ah
)
48 transform_attribute
*ta
= ah2ta(ah
);
56 // When we release AH's we clear out the transform pointer, so if we get here with transform!=NULL somebody
57 // has released an AH we are still using and we will crash very very soon (in our code) if we don't abort here
58 syslog(LOG_ERR
, "over release of SecTransformAttributeRef at %p\n", ah
);
64 CFReleaseNull(ta
->value
);
67 // ta->q already released
71 CFReleaseNull(ta
->connections
);
76 dispatch_release(ta
->semaphore
);
79 if (ta
->attribute_changed_block
)
81 Block_release(ta
->attribute_changed_block
);
84 if (ta
->attribute_validate_block
)
86 Block_release(ta
->attribute_validate_block
);
94 static CFHashCode
ah_set_hash(const void *v
) {
95 return CFHash(ah2ta(static_cast<SecTransformAttributeRef
>(const_cast<void*>(v
)))->name
);
98 static Boolean
ah_set_equal(const void *v1
, const void *v2
) {
99 return CFEqual(ah2ta(static_cast<SecTransformAttributeRef
>(const_cast<void*>(v1
)))->name
, ah2ta(static_cast<SecTransformAttributeRef
>(const_cast<void*>(v2
)))->name
);
102 CFTypeID
transform_attribute::cftype
;
104 SecTransformAttributeRef
Transform::makeAH(transform_attribute
*ta
) {
106 SecTransformAttributeRef ah
= _CFRuntimeCreateInstance(NULL
, transform_attribute::cftype
, sizeof(struct transform_attribute
*), NULL
);
110 *(struct transform_attribute
**)(1 + (CFRuntimeBase
*)ah
) = ta
;
117 static pthread_key_t ah_search_key_slot
;
119 static void destroy_ah_search_key(void *ah
) {
121 pthread_setspecific(ah_search_key_slot
, NULL
);
126 SecTransformAttributeRef
Transform::getAH(SecTransformStringOrAttributeRef attrib
, bool create_ok
, bool create_underscore_ok
)
128 if (CFGetTypeID(attrib
) == transform_attribute::cftype
)
130 return (SecTransformAttributeRef
)attrib
;
133 CFStringRef label
= (CFStringRef
)attrib
;
134 static dispatch_once_t once
= 0;
135 const char *name
= (const char *)"SecTransformAttributeRef";
136 static CFRuntimeClass ahclass
;
137 static CFSetCallBacks tasetcb
;
139 dispatch_once(&once
, ^{
140 ahclass
.className
= name
;
141 ahclass
.copyFormattingDesc
= AttributeHandleFormat
;
142 ahclass
.copyDebugDesc
= AttributeHandleDebugFormat
;
143 ahclass
.finalize
= AttributeHandleFinalize
;
144 transform_attribute::cftype
= _CFRuntimeRegisterClass(&ahclass
);
145 if (transform_attribute::cftype
== _kCFRuntimeNotATypeID
) {
149 tasetcb
.equal
= ah_set_equal
;
150 tasetcb
.hash
= ah_set_hash
;
151 tasetcb
.copyDescription
= ah_set_describe
;
153 pthread_key_create(&ah_search_key_slot
, destroy_ah_search_key
);
156 SecTransformAttributeRef search_for
= pthread_getspecific(ah_search_key_slot
);
159 search_for
= makeAH((transform_attribute
*)malloc(sizeof(transform_attribute
)));
165 bzero(ah2ta(search_for
), sizeof(transform_attribute
));
166 pthread_setspecific(ah_search_key_slot
, search_for
);
171 mAttributes
= CFSetCreateMutable(NULL
, 0, &tasetcb
);
174 ah2ta(search_for
)->name
= label
;
175 SecTransformAttributeRef ah
= static_cast<SecTransformAttributeRef
>(const_cast<void*>(CFSetGetValue(mAttributes
, search_for
)));
176 if (ah
== NULL
&& create_ok
)
178 if (CFStringGetLength(label
) && L
'_' == CFStringGetCharacterAtIndex(label
, 0) && !create_underscore_ok
)
180 // Attributes with a leading _ belong to the Transform system only, not to random 3rd party transforms.
184 transform_attribute
*ta
= static_cast<transform_attribute
*>(malloc(sizeof(transform_attribute
)));
191 ta
->name
= CFStringCreateCopy(NULL
, label
);
197 CFIndex cnt
= CFSetGetCount(mAttributes
);
198 CFSetAddValue(mAttributes
, ah
);
199 if (CFSetGetCount(mAttributes
) != cnt
+1)
201 CFReleaseNull(ta
->name
);
206 CFStringRef qname
= CFStringCreateWithFormat(NULL
, NULL
, CFSTR("%s/%@"), dispatch_queue_get_label(this->mDispatchQueue
), label
);
207 CFIndex used
, sz
= 1+CFStringGetMaximumSizeForEncoding(CFStringGetLength(qname
), kCFStringEncodingUTF8
);
208 UInt8
*qnbuf
= (UInt8
*)alloca(sz
);
209 CFStringGetBytes(qname
, CFRangeMake(0, CFStringGetLength(qname
)), kCFStringEncodingUTF8
, '?', FALSE
, qnbuf
, sz
, &used
);
211 ta
->q
= dispatch_queue_create((char*)qnbuf
, NULL
);
212 CFReleaseNull(qname
);
213 ta
->semaphore
= dispatch_semaphore_create(kMaxPendingTransactions
);
216 ta
->pushback_state
= transform_attribute::pb_empty
;
217 ta
->pushback_value
= NULL
;
219 ta
->connections
= NULL
;
220 ta
->transform
= this;
222 dispatch_set_target_queue(ta
->q
, mDispatchQueue
);
224 ta
->requires_outbound_connection
= 0;
227 ta
->ignore_while_externalizing
= 0;
228 ta
->has_incoming_connection
= 0;
229 ta
->direct_error_handling
= 0;
230 ta
->allow_external_sets
= 0;
231 ta
->has_been_deferred
= 0;
232 ta
->attribute_changed_block
= NULL
;
233 ta
->attribute_validate_block
= NULL
;
239 transform_attribute
*Transform::getTA(SecTransformStringOrAttributeRef attrib
, bool create_ok
)
241 SecTransformAttributeRef ah
= getAH(attrib
, create_ok
);
254 void Transform::TAGetAll(transform_attribute
**attributes
) {
255 CFSetGetValues(mAttributes
, (const void**)attributes
);
256 CFIndex i
, n
= CFSetGetCount(mAttributes
);
257 for(i
= 0; i
< n
; ++i
) {
258 attributes
[i
] = ah2ta(attributes
[i
]);
264 bool Transform::HasNoOutboundConnections()
266 // make an array big enough to hold all of the attributes
267 CFIndex numAttributes
= CFSetGetCount(mAttributes
);
268 transform_attribute
**attributes
= (transform_attribute
**)malloc(numAttributes
*sizeof(transform_attribute
));
270 if (attributes
== NULL
) {
271 // No more memory, we assume it's orphaned
275 TAGetAll(attributes
);
277 // check all of the attributes
279 for (i
= 0; i
< numAttributes
; ++i
)
281 if (attributes
[i
]->connections
&& CFArrayGetCount(attributes
[i
]->connections
) != 0)
295 bool Transform::HasNoInboundConnections()
297 // make an array big enough to hold all of the attributes
298 CFIndex numAttributes
= CFSetGetCount(mAttributes
);
299 transform_attribute
**attributes
= (transform_attribute
**)malloc(numAttributes
*sizeof(transform_attribute
));
301 if (attributes
== NULL
) {
302 // No more memory, we assume it's orphaned
306 TAGetAll(attributes
);
308 // check all of the attributes
310 for (i
= 0; i
< numAttributes
; ++i
)
312 if (attributes
[i
]->has_incoming_connection
)
326 CFIndex
Transform::GetAttributeCount()
328 return CFSetGetCount(mAttributes
);
331 Transform::Transform(CFStringRef transformType
, CFStringRef CFobjectType
) :
332 CoreFoundationObject(CFobjectType
),
334 mIsFinalizing(false),
335 mAlwaysSelfNotify(false),
338 mTypeName(CFStringCreateCopy(NULL
, transformType
))
342 mProcessingPushbacks
= FALSE
;
344 if (internalID
== _kCFRuntimeNotATypeID
) {
345 (void)SecTransformNoData();
346 internalID
= CoreFoundationObject::FindObjectType(gInternalCFObjectName
);
349 // create a name for the transform
352 for (i
= 0; i
< sizeof(rname
) - 1; ++i
)
354 rname
[i
] = RandomChar();
359 char *tname
= const_cast<char*>(CFStringGetCStringPtr(transformType
, kCFStringEncodingUTF8
));
361 CFIndex sz
= 1+CFStringGetMaximumSizeForEncoding(CFStringGetLength(transformType
), kCFStringEncodingUTF8
);
362 tname
= static_cast<typeof(tname
)>(alloca(sz
));
364 CFStringGetCString(transformType
, tname
, sz
, kCFStringEncodingUTF8
);
366 tname
= const_cast<char*>("-");
371 asprintf(&name
, "%s-%s", rname
, tname
);
374 asprintf(&dqName
, "%s-%s", rname
, tname
);
377 asprintf(&aqName
, "aq-%s-%s", rname
, tname
);
379 mDispatchQueue
= dispatch_queue_create(dqName
, NULL
);
380 dispatch_queue_set_specific(mDispatchQueue
, &dispatchQueueToTransformKey
, this, NULL
);
381 // mActivationQueue's job in life is to be suspended until just after this transform is made active.
382 // It's primary use is when a value flowing across a connection isn't sure if the target transform is active yet.
383 mActivationQueue
= dispatch_queue_create(aqName
, NULL
);
384 dispatch_set_target_queue(mActivationQueue
, mDispatchQueue
);
385 dispatch_suspend(mActivationQueue
);
386 mActivationPending
= dispatch_group_create();
388 // set up points for ABORT, DEBUG, INPUT, and OUTPUT
389 AbortAH
= getAH(kSecTransformAbortAttributeName
, true);
390 transform_attribute
*ta
= ah2ta(AbortAH
);
391 ta
->ignore_while_externalizing
= 1;
392 CFStringRef attributeName
= CFStringCreateWithCStringNoCopy(NULL
, name
, 0, kCFAllocatorMalloc
);
393 SetAttributeNoCallback(kSecTransformTransformName
, attributeName
);
394 CFReleaseNull(attributeName
);
399 DebugAH
= getAH(kSecTransformDebugAttributeName
, true);
400 ah2ta(DebugAH
)->ignore_while_externalizing
= 1;
402 ta
= getTA(kSecTransformInputAttributeName
, true);
403 ta
->required
= ta
->deferred
= ta
->stream
= 1;
404 ta
->allow_external_sets
= 0;
406 ta
->has_been_deferred
= 0;
407 ta
= getTA(kSecTransformOutputAttributeName
, true);
408 ta
->requires_outbound_connection
= ta
->stream
= 1;
411 static void run_and_release_finalizer(void *finalizer_block
)
413 ((dispatch_block_t
)finalizer_block
)();
414 Block_release(finalizer_block
);
417 static void set_dispatch_finalizer(dispatch_object_t object
, dispatch_block_t finalizer
)
419 finalizer
= Block_copy(finalizer
);
420 dispatch_set_context(object
, finalizer
);
421 dispatch_set_finalizer_f(object
, run_and_release_finalizer
);
424 void Transform::FinalizePhase2()
429 void Transform::FinalizeForClang()
431 CFIndex numAttributes
= CFSetGetCount(mAttributes
);
432 SecTransformAttributeRef
*handles
= (const void**)malloc(numAttributes
*sizeof(SecTransformAttributeRef
));
434 if (handles
== NULL
) {
435 syslog(LOG_ERR
, "Unable to allocate SecTransformAttributeRef handles in FinalizeForClang");
439 CFSetGetValues(mAttributes
, handles
);
441 for(CFIndex i
= 0; i
< numAttributes
; ++i
) {
442 SecTransformAttributeRef ah
= handles
[i
];
443 transform_attribute
*ta
= ah2ta(ah
);
445 set_dispatch_finalizer(ta
->q
, ^{
446 // NOTE: not done until all pending use of the attribute queue has ended AND retain count is zero
447 ta
->transform
= NULL
;
450 // If there is a pending pushback the attribute queue will be suspended, and needs a kick before it can be destructed.
451 if (__sync_bool_compare_and_swap(&ta
->pushback_state
, transform_attribute::pb_value
, transform_attribute::pb_discard
)) {
452 dispatch_resume(ta
->q
);
454 dispatch_release(ta
->q
);
457 // We might be finalizing a transform as it is being activated, make sure that is complete before we do the rest
458 dispatch_group_notify(mActivationPending
, mDispatchQueue
, ^{
459 if (mActivationQueue
!= NULL
) {
460 // This transform has not been activated (and does not have a activation pending), so we need to resume to activation queue before we can release it
461 dispatch_resume(mActivationQueue
);
462 dispatch_release(mActivationQueue
);
465 set_dispatch_finalizer(mDispatchQueue
, ^{
466 // NOTE: delayed until all pending work items on the transform's queue are complete, and all of the attribute queues have been finalized, and the retain count is zero
469 dispatch_release(mDispatchQueue
);
475 void Transform::Finalize()
477 // When _all_ transforms in the group have been marked as finalizing we can tear down our own context without anyone else in the group sending us values
478 // (NOTE: moved block into member function as clang hits an internal error and declines to compile)
479 dispatch_block_t continue_finalization
= ^{ this->FinalizeForClang(); };
480 dispatch_block_t mark_as_finalizing
= ^{ this->mIsFinalizing
= true; };
482 // Mark the transform as "finalizing" so it knows not to propagate values across connections
483 if (this == dispatch_get_specific(&dispatchQueueToTransformKey
)) {
484 mark_as_finalizing();
486 dispatch_sync(mDispatchQueue
, mark_as_finalizing
);
490 (void)transforms_assume(mGroup
->mIsFinalizing
); // under retain?
491 mGroup
->AddAllChildrenFinalizedCallback(mDispatchQueue
, continue_finalization
);
492 mGroup
->ChildStartedFinalization(this);
494 // a "bare" transform (normally itself a group) still needs to be deconstructed
495 dispatch_async(mDispatchQueue
, continue_finalization
);
499 Transform::~Transform()
501 CFReleaseNull(mAttributes
);
503 CFReleaseNull(mAbortError
);
507 // See if we can catch anything using us after our death
508 mDispatchQueue
= (dispatch_queue_t
)0xdeadbeef;
510 CFReleaseNull(mTypeName
);
512 if (NULL
!= mPushedback
)
514 CFReleaseNull(mPushedback
);
516 dispatch_release(mActivationPending
);
519 CFStringRef
Transform::GetName() CF_RETURNS_NOT_RETAINED
{
520 return (CFStringRef
)GetAttribute(kSecTransformTransformName
);
523 CFTypeID
Transform::GetCFTypeID()
525 return CoreFoundationObject::FindObjectType(CFSTR("SecTransform"));
528 std::string
Transform::DebugDescription()
530 return CoreFoundationObject::DebugDescription() + "|SecTransform|" + StringFromCFString(this->GetName());
533 CFErrorRef
Transform::SendMetaAttribute(SecTransformStringOrAttributeRef key
, SecTransformMetaAttributeType type
, CFTypeRef value
)
535 SecTransformAttributeRef ah
= getAH(key
, true);
536 transform_attribute
*ta
= ah2ta(ah
);
539 case kSecTransformMetaAttributeRequired
:
540 ta
->required
= CFBooleanGetValue((CFBooleanRef
)value
) ? 1 : 0;
543 case kSecTransformMetaAttributeRequiresOutboundConnection
:
544 ta
->requires_outbound_connection
= CFBooleanGetValue((CFBooleanRef
)value
) ? 1 : 0;
547 case kSecTransformMetaAttributeDeferred
:
548 ta
->deferred
= CFBooleanGetValue((CFBooleanRef
)value
) ? 1 : 0;
551 case kSecTransformMetaAttributeStream
:
552 ta
->stream
= CFBooleanGetValue((CFBooleanRef
)value
) ? 1 : 0;
555 case kSecTransformMetaAttributeHasOutboundConnections
:
556 return CreateSecTransformErrorRef(kSecTransformErrorInvalidOperation
, "Can't set kSecTransformMetaAttributeHasOutboundConnections for %@ (or any other attribute)", ah
);
558 case kSecTransformMetaAttributeHasInboundConnection
:
559 return CreateSecTransformErrorRef(kSecTransformErrorInvalidOperation
, "Can't set kSecTransformMetaAttributeHasInboundConnection for %@ (or any other attribute)", ah
);
561 case kSecTransformMetaAttributeCanCycle
:
562 return CreateSecTransformErrorRef(kSecTransformErrorInvalidOperation
, "kSecTransformMetaAttributeCanCycle not yet supported (%@)", ah
);
564 case kSecTransformMetaAttributeExternalize
:
565 ta
->ignore_while_externalizing
= CFBooleanGetValue((CFBooleanRef
)value
) ? 0 : 1;
568 case kSecTransformMetaAttributeValue
:
569 return SetAttributeNoCallback(ah
, value
);
571 case kSecTransformMetaAttributeRef
:
572 return CreateSecTransformErrorRef(kSecTransformErrorInvalidOperation
, "Can't set kSecTransformMetaAttributeRef for %@ (or any other attribute)", ah
);
574 case kSecTransformMetaAttributeName
:
575 return CreateSecTransformErrorRef(kSecTransformErrorInvalidOperation
, "Can't set kSecTransformMetaAttributeName for %@ (or any other attribute)", ah
);
578 return CreateSecTransformErrorRef(kSecTransformErrorInvalidOperation
, "Can't set unknown meta attribute #%d to %@ on %@", type
, value
, key
);
584 CFTypeRef
Transform::GetMetaAttribute(SecTransformStringOrAttributeRef key
, SecTransformMetaAttributeType type
) {
585 SecTransformAttributeRef ah
= getAH(key
, true);
586 transform_attribute
*ta
= ah2ta(ah
);
588 case kSecTransformMetaAttributeRequired
:
589 return (CFTypeRef
)(ta
->required
? kCFBooleanTrue
: kCFBooleanFalse
);
590 case kSecTransformMetaAttributeRequiresOutboundConnection
:
591 return (CFTypeRef
)(ta
->requires_outbound_connection
? kCFBooleanTrue
: kCFBooleanFalse
);
592 case kSecTransformMetaAttributeDeferred
:
593 return (CFTypeRef
)(ta
->deferred
? kCFBooleanTrue
: kCFBooleanFalse
);
594 case kSecTransformMetaAttributeStream
:
595 return (CFTypeRef
)(ta
->stream
? kCFBooleanTrue
: kCFBooleanFalse
);
596 case kSecTransformMetaAttributeHasOutboundConnections
:
597 return (CFTypeRef
)((ta
->connections
&& CFArrayGetCount(ta
->connections
)) ? kCFBooleanTrue
: kCFBooleanFalse
);
598 case kSecTransformMetaAttributeHasInboundConnection
:
599 return (CFTypeRef
)(ta
->has_incoming_connection
? kCFBooleanTrue
: kCFBooleanFalse
);
600 case kSecTransformMetaAttributeCanCycle
:
601 return (CFTypeRef
)kCFBooleanFalse
;
602 case kSecTransformMetaAttributeExternalize
:
603 return (CFTypeRef
)(ta
->ignore_while_externalizing
? kCFBooleanFalse
: kCFBooleanTrue
);
604 case kSecTransformMetaAttributeRef
:
606 case kSecTransformMetaAttributeValue
:
608 case kSecTransformMetaAttributeName
:
611 return CreateSecTransformErrorRef(kSecTransformErrorInvalidOperation
, "Can't get unknown meta attribute #%d from %@", type
, key
);
619 CFErrorRef
Transform::RefactorErrorToIncludeAbortingTransform(CFErrorRef sourceError
)
621 // pull apart the error
622 CFIndex code
= CFErrorGetCode(sourceError
);
623 CFStringRef domain
= CFErrorGetDomain(sourceError
);
624 CFDictionaryRef oldUserInfo
= CFErrorCopyUserInfo(sourceError
);
625 CFMutableDictionaryRef userInfo
= CFDictionaryCreateMutableCopy(NULL
, 0, oldUserInfo
);
626 CFReleaseNull(oldUserInfo
);
628 // add the new key and value to the dictionary
629 CFDictionaryAddValue(userInfo
, kSecTransformAbortOriginatorKey
, GetCFObject());
631 // make a new CFError
632 CFErrorRef newError
= CFErrorCreate(NULL
, domain
, code
, userInfo
);
633 CFReleaseNull(userInfo
);
637 // NOTE: If called prior to execution will schedule a later call to AbortAllTransforms
638 void Transform::AbortJustThisTransform(CFErrorRef abortErr
)
640 (void)transforms_assume(abortErr
);
641 (void)transforms_assume(dispatch_get_specific(&dispatchQueueToTransformKey
) == this);
643 Boolean wasActive
= mIsActive
;
645 if (OSAtomicCompareAndSwapPtr(NULL
, (void *)abortErr
, (void**)&mAbortError
)) {
646 // send an abort message to the attribute so that it can shut down
647 // note that this bypasses the normal processes. The message sent is a notification
648 // that things aren't working well any more, the transform cannot make any other assumption.
650 // mAbortError is released in the destructor which is triggered (in part)
651 // by the dispatch queue finalizer so we don't need a retain/release of
652 // abortErr for the abortAction block, but we do need to retain it
653 // here to match with the release by the destructor.
654 CFRetainSafe(abortErr
);
656 dispatch_block_t abortAction
= ^{
657 // This actually makes the abort happen, it needs to run on the transform's queue while the
658 // transform is executing.
661 // When this abort was first processed we were not executing, so
662 // additional transforms may have been added to our group (indeed,
663 // we may not have had a group at all), so we need to let everyone
664 // know about the problem. This will end up letting ourself (and
665 // maybe some others) know an additional time, but the CompareAndSwap
666 // prevents that from being an issue.
667 this->AbortAllTransforms(abortErr
);
670 SecTransformAttributeRef GCC_BUG_WORKAROUND inputAttributeHandle
= getAH(kSecTransformInputAttributeName
, false);
671 // Calling AttributeChanged directly lets an error "skip ahead" of the input queue,
672 // and even execute if the input queue is suspended pending pushback retries.
673 AttributeChanged(inputAttributeHandle
, abortErr
);
678 // This transform is running, so we use the normal queue (which we are
679 // already executing on)
682 // This transform hasn't run yet, do the work on the activation queue
683 // so it happens as soon as the transforms starts executing.
684 dispatch_async(mActivationQueue
, abortAction
);
687 Debug("%@ set to %@ while processing ABORT=%@, this extra set will be ignored", AbortAH
, abortErr
, mAbortError
);
691 // abort all transforms in the root group & below
692 void Transform::AbortAllTransforms(CFTypeRef err
)
694 Debug("%@ set to %@, aborting\n", AbortAH
, err
);
695 CFErrorRef error
= NULL
;
697 CFTypeRef replacementErr
= NULL
;
699 if (CFGetTypeID(err
) != CFErrorGetTypeID())
701 CFStringRef thisErrorTypeDescription
= CFCopyTypeIDDescription(CFGetTypeID(err
));
702 CFStringRef regularErrorTypeDescription
= CFCopyTypeIDDescription(CFErrorGetTypeID());
703 replacementErr
= err
= CreateSecTransformErrorRef(kSecTransformErrorInvalidType
, "ABORT set to a %@ (%@) not a %@", thisErrorTypeDescription
, err
, regularErrorTypeDescription
);
704 CFReleaseNull(thisErrorTypeDescription
);
705 CFReleaseNull(regularErrorTypeDescription
);
708 error
= RefactorErrorToIncludeAbortingTransform((CFErrorRef
)err
);
712 CFReleaseNull(replacementErr
);
715 GroupTransform
*root
= GetRootGroup();
718 // tell everyone in the (root) group to "AbortJustThisTransform"
719 dispatch_group_t all_aborted
= dispatch_group_create();
720 root
->ForAllNodesAsync(false, all_aborted
, ^(Transform
* t
){
721 t
->AbortJustThisTransform(error
);
723 dispatch_group_notify(all_aborted
, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH
, 0), ^(void) {
724 CFReleaseSafe(error
);
725 dispatch_release(all_aborted
);
730 // We are everyone so we AbortJustThisTransform "directly"
731 // NOTE: this can only happen prior to execution (execution always happens in a group)
732 (void)transforms_assume_zero(mIsActive
);
733 this->AbortJustThisTransform(error
);
739 CFErrorRef
Transform::Disconnect(Transform
* destinationTransform
, CFStringRef myKey
, CFStringRef hisKey
)
741 //CFTypeRef thisTransform = (SecTransformRef) GetCFObject();
743 // find this transform in the backlinks for the destination
746 // now remove the link in the transform dictionary
747 transform_attribute
*src
= getTA(myKey
, true);
748 SecTransformAttributeRef dst
= destinationTransform
->getAH(hisKey
);
750 if (src
->connections
== NULL
)
752 return CreateSecTransformErrorRef(kSecTransformErrorInvalidOperation
, "Cannot find transform in destination.");
755 CFIndex numConnections
= CFArrayGetCount(src
->connections
);
756 for (i
= 0; i
< numConnections
; ++i
)
758 if (CFArrayGetValueAtIndex(src
->connections
, i
) == dst
)
760 CFArrayRemoveValueAtIndex(src
->connections
, i
);
761 numConnections
= CFArrayGetCount(src
->connections
);
764 // clear the has_incoming_connection bit in the destination. We can do this because inputs can have only one connection.
765 transform_attribute
* dstTA
= ah2ta(dst
);
766 dstTA
->has_incoming_connection
= false;
769 if (HasNoInboundConnections() && HasNoOutboundConnections())
771 // we have been orphaned, just remove us
772 mGroup
->RemoveMemberFromGroup(GetCFObject());
781 CFErrorRef
Transform::Connect(GroupTransform
*group
, Transform
* destinationTransform
, CFStringRef destAttr
, CFStringRef srcAttr
)
785 CFErrorRef err
= CreateSecTransformErrorRef(kSecTransformErrorInvalidConnection
, "Can not make connections without a specific group (do not call with group = NULL)");
789 GroupTransform
*newSourceGroup
= mGroup
;
790 GroupTransform
*newDestinationGroup
= destinationTransform
->mGroup
;
792 if (mGroup
== NULL
|| mGroup
== this)
794 newSourceGroup
= group
;
797 if (destinationTransform
->mGroup
== NULL
|| destinationTransform
->mGroup
== destinationTransform
)
799 newDestinationGroup
= group
;
802 if (newSourceGroup
!= newDestinationGroup
&& mGroup
)
804 CFErrorRef err
= CreateSecTransformErrorRef(kSecTransformErrorInvalidConnection
, "Can not make connections between transforms in different groups (%@ is in %@, %@ is in %@)", GetName(), newSourceGroup
->GetName(), destinationTransform
->GetName(), newDestinationGroup
->GetName());
808 if (!validConnectionPoint(srcAttr
)) {
809 CFErrorRef err
= CreateSecTransformErrorRef(kSecTransformErrorInvalidConnection
, "Can not make a connection from non-exported attribute %@ of %@", srcAttr
, this->GetName());
812 if (!destinationTransform
->validConnectionPoint(destAttr
)) {
813 CFErrorRef err
= CreateSecTransformErrorRef(kSecTransformErrorInvalidConnection
, "Can not make a connection to non-exported attribute %@ of %@", destAttr
, destinationTransform
->GetName());
817 mGroup
= newSourceGroup
;
818 destinationTransform
->mGroup
= newDestinationGroup
;
820 // NOTE: this fails on OOM
821 group
->AddMemberToGroup(this->GetCFObject());
822 group
->AddMemberToGroup(destinationTransform
->GetCFObject());
824 transform_attribute
*src
= this->getTA(srcAttr
, true);
825 SecTransformAttributeRef dst
= destinationTransform
->getAH(destAttr
);
827 if (!src
->connections
)
829 src
->connections
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
831 CFArrayAppendValue(src
->connections
, dst
);
833 ah2ta(dst
)->has_incoming_connection
= 1;
839 bool Transform::validConnectionPoint(CFStringRef attributeName
)
844 // NoCallback == don't call this transform's Do function, but DO call the Do functions of connected attributes
845 // SetAttribute eventually calls SetAttributeNoCallback
846 CFErrorRef
Transform::SetAttributeNoCallback(SecTransformStringOrAttributeRef key
, CFTypeRef value
)
848 SecTransformAttributeRef ah
= getAH(key
, true);
853 transform_attribute
*ta
= ah2ta(ah
);
855 if (ah
== AbortAH
&& value
&& (mIsActive
|| !ta
->deferred
))
857 AbortAllTransforms(value
);
858 return CreateSecTransformErrorRef(kSecTransformErrorAbortInProgress
, "Abort started");
861 bool do_propagate
= true;
863 if (!ta
->has_been_deferred
)
865 bool doNotRetain
= false;
869 CFStringRef name
= ta
->name
;
870 if (CFGetTypeID(value
) == CFReadStreamGetTypeID())
872 CFTypeRef src
= StreamSource::Make((CFReadStreamRef
) value
, this, name
);
874 do_propagate
= false;
875 ta
->has_been_deferred
= 1;
878 else if (ta
->deferred
&& !mIsActive
)
882 Debug("%@ deferred value=%p\n", ah
, value
);
885 CFTypeRef src
= SingleShotSource::Make(value
, this, name
);
886 ta
->has_been_deferred
= 1;
888 // the old value will be release when Transform::Do terminates
891 do_propagate
= false;
896 ta
->has_been_deferred
= 0;
900 if (ta
->value
!= value
) {
901 if (value
&& !doNotRetain
) {
905 CFReleaseNull(ta
->value
);
912 // propagate the changes out to all connections
913 if (ta
->connections
&& mIsActive
&& do_propagate
&& !(mAbortError
|| mIsFinalizing
))
915 Debug("Propagating from %@ to %@\n", ah
, ta
->connections
);
916 CFIndex i
, numConnections
= CFArrayGetCount(ta
->connections
);
917 for(i
= 0; i
< numConnections
; ++i
) {
918 SecTransformAttributeRef ah
= static_cast<SecTransformAttributeRef
>(const_cast<void *>(CFArrayGetValueAtIndex(ta
->connections
, i
)));
919 Transform
*tt
= ah2ta(ah
)->transform
;
924 tt
->SetAttribute(ah
, value
);
928 dispatch_block_t setAttribute
= ^{
929 tt
->SetAttribute(ah
, value
);
931 // Here the target queue might not be activated yet, we can't
932 // look directly at the other transform's ActivationQueue as
933 // it might activate (or Finalize!) as we look, so just ask
934 // the other transform to deal with it.
935 dispatch_async(ah2ta(ah
)->q
, ^(void) {
936 // This time we are on the right queue to know this is the real deal
940 dispatch_async(ah2ta(ah
)->transform
->mActivationQueue
, setAttribute
);
951 // external sets normally fail if the transform is running
952 CFErrorRef
Transform::ExternalSetAttribute(CFTypeRef key
, CFTypeRef value
)
956 return this->SetAttribute(key
, value
);
960 SecTransformAttributeRef ah
= getAH(key
, false);
961 if (ah
!= NULL
&& ah2ta(ah
)->allow_external_sets
)
963 return this->SetAttribute(static_cast<CFTypeRef
>(ah
), value
);
967 return CreateSecTransformErrorRef(kSecTransformTransformIsExecuting
, "%@ can not be set while %@ is executing", ah
, this->GetName());
973 // queue up the setting of the key and value
974 CFErrorRef
Transform::SetAttribute(CFTypeRef key
, CFTypeRef value
)
978 CFErrorRef result
= CreateSecTransformErrorRef(kSecTransformErrorAborted
, "ABORT has been sent to the transform (%@)", mAbortError
);
979 CFAutorelease(result
);
983 // queue up the setting of the key and value
984 SecTransformAttributeRef ah
;
985 if (CFGetTypeID(key
) == transform_attribute::cftype
)
989 else if (CFGetTypeID(key
) == CFStringGetTypeID())
991 ah
= getAH(static_cast<CFStringRef
>(key
));
994 CFErrorRef result
= CreateSecTransformErrorRef(kSecTransformErrorUnsupportedAttribute
, "Can't set attribute %@ in transform %@", key
, GetName());
995 CFAutorelease(result
);
1001 CFErrorRef result
= CreateSecTransformErrorRef(kSecTransformErrorInvalidType
, "Transform::SetAttribute called with %@, requires a string or an AttributeHandle", key
);
1002 CFAutorelease(result
);
1006 // Do this after the error check above so we don't leak
1007 CFRetainSafe(value
); // if we use dispatch_async we need to own the value (the matching release is in the set block)
1009 transform_attribute
*ta
= ah2ta(ah
);
1011 dispatch_block_t set
= ^{
1013 dispatch_semaphore_signal(ta
->semaphore
);
1014 CFReleaseSafe(value
);
1018 // when the transform is active, set attributes asynchronously. Otherwise, we are doing
1019 // initialization and must wait for the operation to complete.
1022 dispatch_async(ta
->q
, set
);
1026 dispatch_sync(ta
->q
, set
);
1028 if (dispatch_semaphore_wait(ta
->semaphore
, dispatch_time(DISPATCH_TIME_NOW
, NSEC_PER_SEC
))) {
1029 Debug("Send from %@ to %@ is still waiting\n", GetName(), ah
);
1030 dispatch_semaphore_wait(ta
->semaphore
, DISPATCH_TIME_FOREVER
);
1033 // Return the best available status (which will be NULL if we haven't aborted, or stated an
1034 // intent to abort when execution starts)
1036 // The value of the ABORT attribute can differ from mAbortError, first if a transform is aborted
1037 // prior to running the general abort mechanic is deferred until execution. Second during
1038 // execution the abort logic avoids most of the normal processing. Third, and most importantly
1039 // during an abort the exact error that gets generated will differ from the value sent to ABORT
1040 // (for example if a non-CFError was sent...plus even if it was a CFError we annotate that error).
1045 CFErrorRef
Transform::SendAttribute(SecTransformStringOrAttributeRef key
, CFTypeRef value
)
1047 return SetAttributeNoCallback(key
, value
);
1052 CFTypeRef
Transform::GetAttribute(SecTransformStringOrAttributeRef key
)
1054 struct transform_attribute
*ta
= getTA(key
, false);
1055 if (ta
== NULL
|| ta
->value
== NULL
) {
1059 if (CFGetTypeID(ta
->value
) == internalID
)
1061 // this is one of our internal objects, so get the value from it
1062 Source
* source
= (Source
*) CoreFoundationHolder::ObjectFromCFType(ta
->value
);
1063 return source
->GetValue();
1071 CFErrorRef
Transform::Pushback(SecTransformAttributeRef ah
, CFTypeRef value
)
1073 CFErrorRef result
= NULL
;
1074 transform_attribute
*ta
= ah2ta(ah
);
1075 if (!(ta
->pushback_state
== transform_attribute::pb_empty
|| ta
->pushback_state
== transform_attribute::pb_repush
))
1077 CFErrorRef error
= fancy_error(kSecTransformErrorDomain
, kSecTransformErrorInvalidOperation
, CFSTR("Can not pushback new value until old value has been processed"));
1078 SetAttribute(kSecTransformAbortAttributeName
, error
);
1081 if (value
== NULL
&& ta
->pushback_value
== NULL
&& ta
->pushback_state
== transform_attribute::pb_repush
)
1083 ta
->pushback_state
= transform_attribute::pb_presented_once
;
1086 ta
->pushback_state
= transform_attribute::pb_value
;
1090 CFRetainSafe(value
);
1092 ta
->pushback_value
= value
;
1093 dispatch_suspend(ta
->q
);
1096 mPushedback
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
1098 CFArrayAppendValue(mPushedback
, ah
);
1102 void Transform::try_pushbacks() {
1103 if (!mPushedback
|| !CFArrayGetCount(mPushedback
)) {
1104 mProcessingPushbacks
= FALSE
;
1108 CFArrayRef pb
= (CFArrayRef
)mPushedback
;
1109 mPushedback
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
1110 CFIndex i
, n
= CFArrayGetCount(pb
);
1112 for(i
= 0; i
< n
; ++i
)
1114 SecTransformAttributeRef ah
= CFArrayGetValueAtIndex(pb
, i
);
1115 transform_attribute
*ta
= ah2ta(ah
);
1116 ta
->pushback_state
= transform_attribute::pb_repush
;
1117 CFTypeRef v
= ta
->pushback_value
;
1118 ta
->pushback_value
= NULL
;
1124 if (ta
->pushback_state
== transform_attribute::pb_repush
) {
1125 ta
->pushback_state
= transform_attribute::pb_empty
;
1128 // NOTE: a successful repush needs the queue unsuspended so it can run.
1129 // A failed repush has suspended the queue an additional time, so we
1130 // still need to resume it.
1131 dispatch_resume(ta
->q
);
1136 if (succeeded
&& CFArrayGetCount(mPushedback
)) {
1137 // some attribute changed while we proceeded the last batch of pushbacks, so any "new" pushbacks are eligible to run again.
1138 // In theory the ones that were pushed after the last success don't need to be re-run but that isn't a big deal.
1139 dispatch_async(mDispatchQueue
, ^{ try_pushbacks(); });
1141 mProcessingPushbacks
= FALSE
;
1145 void Transform::Debug(const char *cfmt
, ...) {
1146 CFTypeRef d
= ah2ta(DebugAH
)->value
;
1148 CFWriteStreamRef out
= NULL
;
1149 if (CFGetTypeID(d
) == CFWriteStreamGetTypeID()) {
1150 out
= (CFWriteStreamRef
)d
;
1152 static dispatch_once_t once
;
1153 static CFWriteStreamRef StdErrWriteStream
;
1154 dispatch_once(&once
, ^{
1155 CFURLRef p
= CFURLCreateWithFileSystemPath(NULL
, CFSTR("/dev/stderr"), kCFURLPOSIXPathStyle
, FALSE
);
1156 StdErrWriteStream
= CFWriteStreamCreateWithFile(NULL
, p
);
1157 CFWriteStreamOpen(StdErrWriteStream
);
1160 out
= StdErrWriteStream
;
1166 CFStringRef fmt
= CFStringCreateWithCString(NULL
, cfmt
, kCFStringEncodingUTF8
);
1167 CFStringRef str
= CFStringCreateWithFormatAndArguments(NULL
, NULL
, fmt
, ap
);
1172 CFIndex sz
= CFStringGetMaximumSizeForEncoding(CFStringGetLength(str
), kCFStringEncodingUTF8
);
1176 bool needs_free
= true;
1177 buf
= (unsigned char*)malloc(sz
);
1179 CFStringGetBytes(str
, CFRangeMake(0, CFStringGetLength(str
)), kCFStringEncodingUTF8
, '?', FALSE
, buf
, sz
, &used
);
1181 buf
= (unsigned char *)"malloc failure during Transform::Debug\n";
1185 static dispatch_once_t once
;
1186 static dispatch_queue_t print_q
;
1187 dispatch_once(&once
, ^{
1188 print_q
= dispatch_queue_create("com.apple.security.debug.print_queue", 0);
1189 dispatch_set_target_queue((dispatch_object_t
)print_q
, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW
, 0));
1192 dispatch_async(print_q
, ^{
1193 CFWriteStreamWrite(out
, buf
, used
);
1203 void Transform::Do(SecTransformAttributeRef ah
, CFTypeRef value
)
1205 transform_attribute
*ta
= ah2ta(ah
);
1206 if (ta
->pushback_state
== transform_attribute::pb_discard
)
1210 (void)transforms_assume(dispatch_get_current_queue() == ((ta
->pushback_state
== transform_attribute::pb_repush
) ? mDispatchQueue
: ta
->q
));
1214 Debug("Ignoring value %p sent to %@ (on queue %s) during finalization", value
, ah
, dispatch_queue_get_label(dispatch_get_current_queue()));
1218 SetAttributeNoCallback(ah
, value
);
1219 // While an abort is in progress things can get into bad
1220 // states if we allow normal processing so we throw anything
1221 // on the floor except CFErrorRef or NULL vales sent to
1222 // ABORT or INPUT (we need to process them to let the
1223 // transform shut down correctly)
1224 if (mAbortError
&& (!(ah
== this->AbortAH
|| ah
== getTA(CFSTR("INPUT"), true)) && (value
== NULL
|| CFGetTypeID(value
) != CFErrorGetTypeID())))
1227 Debug("Ignoring value (%@) sent to %@ during abort\n", value
, ah
);
1229 Debug("Ignoring NULL sent to %@ during abort\n", ah
);
1234 if (mIsActive
|| (mAlwaysSelfNotify
&& !ta
->deferred
))
1236 Debug("AttributeChanged: %@ (%s) = %@\n", ah
, mIsActive
? "is executing" : "self notify set", value
? value
: (CFTypeRef
)CFSTR("(NULL)"));
1237 AttributeChanged(ah
, value
);
1240 if (mPushedback
&& CFArrayGetCount(mPushedback
) && !mProcessingPushbacks
)
1242 Debug("will process pushbacks (%@) later\n", mPushedback
);
1243 mProcessingPushbacks
= TRUE
;
1244 dispatch_async(mDispatchQueue
, ^{ try_pushbacks(); });
1251 void Transform::AttributeChanged(CFStringRef name
, CFTypeRef value
)
1255 void Transform::AttributeChanged(SecTransformAttributeRef ah
, CFTypeRef value
)
1257 AttributeChanged(ah2ta(ah
)->name
, value
);
1260 CFArrayRef
Transform::GetAllAH() {
1261 CFIndex cnt
= CFSetGetCount(mAttributes
);
1262 const void **values
= (const void **)alloca(sizeof(void*)*cnt
);
1263 CFSetGetValues(mAttributes
, values
);
1264 return CFArrayCreate(NULL
, values
, cnt
, &kCFTypeArrayCallBacks
);
1267 CFTypeRef
Transform::Execute(dispatch_queue_t deliveryQueue
, SecMessageBlock deliveryBlock
, CFErrorRef
* errorRef
)
1271 CFTypeRef g
= GroupTransform::Make();
1272 mGroup
= (GroupTransform
*)CoreFoundationHolder::ObjectFromCFType(g
);
1273 mGroup
->AddMemberToGroup(this->GetCFObject());
1274 SecMessageBlock smb
= ^(CFTypeRef message
, CFErrorRef error
, Boolean isFinal
)
1276 deliveryBlock(message
, error
, isFinal
);
1279 dispatch_async(this->mDispatchQueue
, ^{
1285 CFTypeRef ret
= this->Execute(deliveryQueue
, deliveryBlock
? smb
: (SecMessageBlock
) NULL
, errorRef
);
1299 *errorRef
= CreateSecTransformErrorRef(kSecTransformTransformIsExecuting
, "The %@ transform has already executed, it may not be executed again.", GetName());
1305 // Do a retain on our parent since we are using it
1306 GroupTransform
*rootGroup
= GetRootGroup();
1307 CFRetainSafe(rootGroup
->GetCFObject());
1309 CFTypeRef result
= NULL
;
1311 CFTypeRef monitorRef
= BlockMonitor::Make(deliveryQueue
, deliveryBlock
);
1313 __block CFStringRef outputAttached
= NULL
;
1315 dispatch_queue_t p2
= dispatch_queue_create("activate phase2", NULL
);
1316 dispatch_queue_t p3
= dispatch_queue_create("activate phase3", NULL
);
1317 dispatch_suspend(p2
);
1318 dispatch_suspend(p3
);
1319 // walk the transform, doing phase1 activating as we go, and queueing phase2 and phase3 work
1320 CFErrorRef temp
= TraverseTransform(NULL
, ^(Transform
*t
){
1321 return t
->ExecuteOperation(outputAttached
, (SecMonitorRef
)monitorRef
, p2
, p3
);
1323 // ExecuteOperation is not called for the outer group, so we need to manually set mISActive for it.
1324 rootGroup
->mIsActive
= true;
1325 rootGroup
->StartingExecutionInGroup();
1326 dispatch_resume(p2
);
1327 dispatch_sync(p2
, ^{ dispatch_resume(p3
); });
1328 dispatch_sync(p3
, ^{ dispatch_release(p2
); });
1329 dispatch_release(p3
);
1336 // It is safe to keep the monitors attached, because it is invalid to try to execute again, BUT
1337 // we do need to release the reference to the group that the monitor would normally release
1338 // when it processes the final message.
1339 CFReleaseSafe(rootGroup
->GetCFObject());
1340 CFReleaseNull(monitorRef
);
1341 rootGroup
->StartedExecutionInGroup(false);
1345 dispatch_group_t initialized
= dispatch_group_create();
1346 rootGroup
->ForAllNodesAsync(true, initialized
, ^(Transform
*t
) {
1350 dispatch_group_notify(initialized
, rootGroup
->mDispatchQueue
, ^{
1351 dispatch_release(initialized
);
1352 dispatch_group_t activated
= dispatch_group_create();
1353 dispatch_group_enter(activated
);
1354 dispatch_async(rootGroup
->mDispatchQueue
, ^{
1355 rootGroup
->ForAllNodesAsync(true, activated
, ^(Transform
*t
) {
1356 t
->ActivateInputs();
1358 dispatch_group_leave(activated
);
1360 dispatch_group_notify(activated
, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT
, 0), ^{
1361 dispatch_release(activated
);
1362 // once we have been activated (but not before!), the monitor belongs to the group, and we can drop our claim
1363 CFReleaseSafe(monitorRef
);
1364 rootGroup
->StartedExecutionInGroup(true);
1372 void Transform::Initialize()
1376 static void ActivateInputs_set(const void *v
, void *unused
) {
1377 transform_attribute
*ta
= static_cast<transform_attribute
*>(ah2ta(const_cast<void *>(v
)));
1378 if (ta
->value
&& internalID
== CFGetTypeID(ta
->value
)) {
1379 Source
* s
= (Source
*) CoreFoundationHolder::ObjectFromCFType(ta
->value
);
1384 void Transform::ActivateInputs()
1386 (void)transforms_assume_zero(mIsActive
&& this != dispatch_get_specific(&dispatchQueueToTransformKey
));
1388 // now run all of the forward links
1389 if (!mIsFinalizing
) {
1390 CFSetApplyFunction(mAttributes
, ActivateInputs_set
, NULL
);
1394 CFErrorRef
Transform::ForAllNodes(bool parallel
, bool includeOwningGroup
, Transform::TransformOperation op
)
1396 GroupTransform
*g
= GetRootGroup();
1398 return g
->ForAllNodes(parallel
, includeOwningGroup
, op
);
1404 CFErrorRef
Transform::TraverseTransform(CFMutableSetRef visited
, TransformOperation t
)
1406 return ForAllNodes(true, true, t
);
1409 CFErrorRef
Transform::ExecuteOperation(CFStringRef
&outputAttached
, SecMonitorRef output
, dispatch_queue_t phase2
, dispatch_queue_t phase3
)
1412 // top level groups are special, and don't go through this path.
1416 if (!TransformCanExecute())
1418 // oops, this transform isn't ready to go
1419 return CreateSecTransformErrorRef(kSecTransformErrorNotInitializedCorrectly
, "The transform %@ was not ready for execution.", GetName());
1422 // check to see if required attributes are connected or set
1423 CFIndex i
, numAttributes
= CFSetGetCount(mAttributes
);
1424 transform_attribute
**attributes
= (transform_attribute
**)alloca(numAttributes
* sizeof(transform_attribute
*));
1425 TAGetAll(attributes
);
1426 CFMutableArrayRef still_need
= NULL
;
1427 for(i
= 0; i
< numAttributes
; ++i
) {
1428 transform_attribute
*ta
= attributes
[i
];
1429 if (ta
->required
&& ta
->value
== NULL
&& !ta
->has_incoming_connection
) {
1431 still_need
= CFArrayCreateMutable(NULL
, i
, &kCFTypeArrayCallBacks
);
1433 CFArrayAppendValue(still_need
, ta
->name
);
1437 CFStringRef elist
= CFStringCreateByCombiningStrings(NULL
, still_need
, CFSTR(", "));
1438 CFErrorRef err
= CreateSecTransformErrorRef(kSecTransformErrorMissingParameter
, "Can not execute %@, missing required attributes: %@", GetName(), elist
);
1439 CFReleaseNull(elist
);
1440 CFReleaseNull(still_need
);
1444 // see if we can attach our output here (note mAttributes may have changed)
1445 numAttributes
= CFSetGetCount(mAttributes
);
1446 attributes
= (transform_attribute
**)alloca(numAttributes
* sizeof(transform_attribute
*));
1447 TAGetAll(attributes
);
1448 for (i
= 0; i
< numAttributes
; ++i
)
1450 transform_attribute
*ta
= attributes
[i
];
1451 CFIndex arraySize
= ta
->connections
? CFArrayGetCount(ta
->connections
) : 0;
1452 if (arraySize
== 0 && ta
->requires_outbound_connection
)
1454 if (CFStringCompare(ta
->name
, kSecTransformOutputAttributeName
, 0) == kCFCompareEqualTo
) {
1455 // this is a place where we can hook up our output -- maybe
1458 // oops, we've already done that.
1459 return CreateSecTransformErrorRef(kSecTransformErrorMoreThanOneOutput
, "Both %@ and %@ have loose outputs, attach one to something", outputAttached
, ta
->transform
->GetName());
1461 // Delay the connect until after ForAllNodes returns
1462 dispatch_async(phase2
, ^{
1463 SecTransformConnectTransformsInternal(mGroup
->GetCFObject(),
1464 GetCFObject(), kSecTransformOutputAttributeName
,
1465 output
, kSecTransformInputAttributeName
);
1467 outputAttached
= ta
->transform
->GetName();
1469 // activate the attached monitor
1470 Monitor
* m
= (Monitor
*) CoreFoundationHolder::ObjectFromCFType(output
);
1471 m
->mIsActive
= true;
1473 // add the monitor to the output so that it doesn't get activated twice
1475 return CreateSecTransformErrorRef(kSecTransformErrorNotInitializedCorrectly
, "Attribute %@ (in %@) requires an outbound connection and doesn't have one", ta
->name
, GetName());
1482 // Delay activation until after the Monitor is connected
1483 dispatch_async(phase3
, ^{
1492 void Transform::DoPhase3Activation()
1494 this->mIsActive
= true;
1495 // execution has now truly started ("mIsActive is true")
1496 CFErrorRef initError
= TransformStartingExecution();
1499 // Oops, now execution is about to grind to a halt
1500 this->SendAttribute(AbortAH
, initError
);
1503 dispatch_resume(this->mActivationQueue
);
1504 dispatch_group_async(this->mActivationPending
, this->mActivationQueue
, ^{
1505 dispatch_release(this->mActivationQueue
);
1506 this->mActivationQueue
= NULL
;
1512 // This would be best expressed as a block, but we seem to run into compiler errors
1513 void Transform::phase3Activation()
1515 dispatch_async(this->mDispatchQueue
, ^
1517 DoPhase3Activation();
1522 Boolean
Transform::TransformCanExecute()
1529 CFErrorRef
Transform::TransformStartingExecution()
1536 bool Transform::IsExternalizable()
1541 static const void *CFTypeOrNULLRetain(CFAllocatorRef allocator
, const void *value
) {
1542 return CFRetainSafe(value
);
1545 static void CFTypeOrNULLRelease(CFAllocatorRef allocator
, const void *value
) {
1546 CFReleaseNull(value
);
1549 static CFStringRef
CFTypeOrNULLCopyDescription (const void *value
) {
1550 if (value
!= NULL
) {
1551 return CFCopyDescription(value
);
1553 return CFSTR("NULL");
1557 static Boolean
CFTypeOrNULLEqual(const void *value1
, const void *value2
) {
1558 if (value1
== NULL
&& value2
== NULL
) {
1561 if (value1
== NULL
|| value2
== NULL
) {
1564 return CFEqual(value1
, value2
);
1569 // Returns a dictionary of all the meta attributes that will need to be reset on a RestoreState
1570 CFDictionaryRef
Transform::GetAHDictForSaveState(SecTransformStringOrAttributeRef key
)
1572 SecTransformMetaAttributeType types
[] =
1574 kSecTransformMetaAttributeRequired
,
1575 kSecTransformMetaAttributeRequiresOutboundConnection
,
1576 kSecTransformMetaAttributeDeferred
,
1577 kSecTransformMetaAttributeStream
,
1578 kSecTransformMetaAttributeCanCycle
,
1579 kSecTransformMetaAttributeValue
1582 CFIndex i
, cnt
= sizeof(types
)/sizeof(SecTransformMetaAttributeType
);
1583 CFTypeRef values
[cnt
];
1584 CFNumberRef keys
[cnt
];
1587 // NOTE: we save meta attributes that are in their "default" state on purpose because the
1588 // default may change in the future and we definitely want to restore the default values at
1589 // time of save (i.e. if "stream=1" is the 10.7 default, but "stream=0" becomes the 10.8
1590 // default we want to load all old transforms with stream=1, the simplest way to do that is
1591 // to store all values, not just non-default values)
1592 for(i
= 0; i
< cnt
; ++i
)
1594 values
[i
] = GetMetaAttribute(key
, types
[i
]);
1595 int tmp
= (int)types
[i
];
1596 keys
[i
] = CFNumberCreate(NULL
, kCFNumberIntType
, &tmp
);
1599 static CFDictionaryValueCallBacks CFTypeOrNULL
;
1600 static dispatch_once_t once
;
1601 dispatch_block_t b
=
1603 CFTypeOrNULL
.version
= 0;
1604 CFTypeOrNULL
.retain
= CFTypeOrNULLRetain
;
1605 CFTypeOrNULL
.release
= CFTypeOrNULLRelease
;
1606 CFTypeOrNULL
.copyDescription
= CFTypeOrNULLCopyDescription
;
1607 CFTypeOrNULL
.equal
= CFTypeOrNULLEqual
;
1609 dispatch_once(&once
, b
);
1611 CFDictionaryRef ret
= CFDictionaryCreate(NULL
, (const void**)&keys
, (const void**)&values
, cnt
, &kCFTypeDictionaryKeyCallBacks
, &CFTypeOrNULL
);
1613 for(i
= 0; i
< cnt
; ++i
)
1615 CFReleaseNull(keys
[i
]);
1621 // return everything that doesn't have ignore_while_externalizing set
1622 CFDictionaryRef
Transform::CopyState()
1624 CFIndex i
, j
, cnt
= CFSetGetCount(mAttributes
);
1625 transform_attribute
**attrs
= (transform_attribute
**)malloc(cnt
*sizeof(transform_attribute
));
1626 CFStringRef
*names
= (CFStringRef
*)malloc(cnt
*sizeof(CFStringRef
));
1627 CFDictionaryRef
*values
= (CFDictionaryRef
*)malloc(sizeof(CFDictionaryRef
) * cnt
);
1629 if (attrs
== NULL
|| names
== NULL
|| values
== NULL
) {
1637 for(i
= j
= 0; i
< cnt
; ++i
)
1639 transform_attribute
*ta
= attrs
[i
];
1640 if (!ta
->ignore_while_externalizing
)
1642 names
[j
] = ta
->name
;
1643 values
[j
++] = GetAHDictForSaveState(ta
->name
);
1647 CFDictionaryRef result
= CFDictionaryCreate(NULL
, (const void**)&names
, (const void**)&values
, j
, &kCFCopyStringDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
1651 for(i
= j
= 0; i
< cnt
; ++i
)
1653 transform_attribute
*ta
= attrs
[i
];
1654 if (!ta
->ignore_while_externalizing
)
1656 CFReleaseNull(values
[j
++]);
1667 void Transform::RestoreState(CFDictionaryRef state
)
1669 CFIndex i
, cnt
= CFDictionaryGetCount(state
);
1671 **keys
= (const void **)alloca(sizeof(void*)*cnt
),
1672 **values
= (const void **)alloca(sizeof(void*)*cnt
);
1674 CFDictionaryGetKeysAndValues(state
, keys
, values
);
1676 // Open issue -- do we need to do anything to values that are already set, but are not in "state"?
1677 // this isn't an issue right now, which is only used on the SecTransformCopyExternalRepresentation path which starts with brand new objects,
1678 // it only becomes an issue if we add a ResetFromState, or use it internally in that role.
1680 for(i
= 0; i
< cnt
; i
++)
1682 SecTransformAttributeRef ah
= getAH(keys
[i
]);
1689 CFIndex j
, meta_cnt
= CFDictionaryGetCount((CFDictionaryRef
)values
[i
]);
1690 const void **types
= (const void**)alloca(sizeof(void*)*meta_cnt
), **meta_values
= (const void**)alloca(sizeof(void*)*meta_cnt
);
1691 CFDictionaryGetKeysAndValues((CFDictionaryRef
)values
[i
], types
, meta_values
);
1694 for(j
= 0; j
< meta_cnt
; ++j
)
1696 CFNumberGetValue((CFNumberRef
)types
[j
], kCFNumberIntType
, &t
);
1697 if (t
== kSecTransformMetaAttributeValue
)
1699 if (meta_values
[j
]) {
1700 // SendMetaAttribute doesn't activate the callbacks
1701 SetAttribute(ah
, meta_values
[j
]);
1706 CFErrorRef result
= SendMetaAttribute(ah
, (SecTransformMetaAttributeType
)t
, meta_values
[j
]);
1707 CFReleaseNull(result
); // see <rdar://problem/8741628> Transform::RestoreState is ignoring error returns
1711 CFErrorRef result
= SendMetaAttribute(ah
, kSecTransformMetaAttributeExternalize
, kCFBooleanTrue
);
1712 CFReleaseNull(result
); // see <rdar://problem/8741628> Transform::RestoreState is ignoring error returns
1716 GroupTransform
* Transform::GetRootGroup()
1718 GroupTransform
*g
= mGroup
;
1724 if (CFGetTypeID(this->GetCFObject()) == SecGroupTransformGetTypeID()) {
1725 return (GroupTransform
*)this;
1731 CFDictionaryRef
Transform::GetCustomExternalData()
1736 void Transform::SetCustomExternalData(CFDictionaryRef customData
)
1741 CFDictionaryRef
Transform::Externalize(CFErrorRef
* error
)
1745 return (CFDictionaryRef
)CreateSecTransformErrorRef(kSecTransformTransformIsExecuting
, "The %@ transform is executing, you need to externalize it prior to execution", GetName());
1748 // make arrays to hold the transforms and the connections
1749 __block CFMutableArrayRef transforms
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
1750 __block CFMutableArrayRef connections
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
1751 GroupTransform
*root
= GetRootGroup();
1753 CFErrorRef err
= ForAllNodes(false, true, ^(Transform
*t
) {
1755 return t
->ProcessExternalize(transforms
, connections
);
1757 return (CFErrorRef
)NULL
;
1762 // Really? This just seems like a bad idea
1772 // make a dictionary to hold the output
1773 CFMutableDictionaryRef output
= CFDictionaryCreateMutable(NULL
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
1774 CFDictionaryAddValue(output
, EXTERN_TRANSFORM_TRANSFORM_ARRAY
, transforms
);
1775 CFDictionaryAddValue(output
, EXTERN_TRANSFORM_CONNECTION_ARRAY
, connections
);
1778 CFReleaseNull(connections
);
1779 CFReleaseNull(transforms
);
1784 CFErrorRef
Transform::ProcessExternalize(CFMutableArrayRef transforms
, CFMutableArrayRef connections
)
1786 if (!IsExternalizable()) {
1790 CFDictionaryRef state
= CopyState();
1791 if (state
&& CFGetTypeID(state
) == CFErrorGetTypeID()) {
1792 return (CFErrorRef
)state
;
1795 // make a dictionary to hold the name, type, and state of this node
1796 CFMutableDictionaryRef node
= CFDictionaryCreateMutable(NULL
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
1797 CFDictionaryAddValue(node
, EXTERN_TRANSFORM_NAME
, GetName());
1799 CFTypeRef type
= CFStringCreateCopy(NULL
, mTypeName
);
1800 CFDictionaryAddValue(node
, EXTERN_TRANSFORM_TYPE
, type
);
1801 CFReleaseNull(type
);
1805 CFDictionaryAddValue(node
, EXTERN_TRANSFORM_STATE
, state
);
1806 CFReleaseNull(state
);
1809 CFDictionaryRef customItems
= GetCustomExternalData();
1810 if (NULL
!= customItems
)
1812 CFDictionaryAddValue(node
, EXTERN_TRANSFORM_CUSTOM_EXPORTS_DICTIONARY
, customItems
);
1813 CFReleaseNull(customItems
);
1816 // append the resulting dictionary to the node list
1817 CFArrayAppendValue(transforms
, node
);
1818 CFReleaseNull(node
);
1820 // now walk the attribute list
1821 CFIndex numAttributes
= CFSetGetCount(mAttributes
);
1822 transform_attribute
**attributes
= (transform_attribute
**)malloc(numAttributes
*sizeof(transform_attribute
));
1824 if (attributes
== NULL
) {
1825 return GetNoMemoryErrorAndRetain();
1828 TAGetAll(attributes
);
1832 // walk the forward links
1833 for (i
= 0; i
< numAttributes
; ++i
)
1835 CFIndex arraySize
= attributes
[i
]->connections
? CFArrayGetCount(attributes
[i
]->connections
) : 0;
1839 for (j
= 0; j
< arraySize
; ++j
)
1841 transform_attribute
*ta
= ah2ta((SecTransformAttributeRef
)CFArrayGetValueAtIndex(attributes
[i
]->connections
, j
));
1843 if (!ta
->transform
->IsExternalizable()) {
1844 // just pretend non-externalizable transforms don't even exist. Don't write out connections, and don't talk to them about externalizing.
1848 // add this forward connection to the array -- make a dictionary
1849 CFMutableDictionaryRef connection
=
1850 CFDictionaryCreateMutable(NULL
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
1852 CFDictionaryAddValue(connection
, EXTERN_TRANSFORM_FROM_NAME
, GetName());
1853 CFDictionaryAddValue(connection
, EXTERN_TRANSFORM_FROM_ATTRIBUTE
, attributes
[i
]->name
);
1854 CFDictionaryAddValue(connection
, EXTERN_TRANSFORM_TO_NAME
, ta
->transform
->GetName());
1855 CFDictionaryAddValue(connection
, EXTERN_TRANSFORM_TO_ATTRIBUTE
, ta
->name
);
1857 CFArrayAppendValue(connections
, connection
);
1858 CFReleaseNull(connection
);