darling-opendirectory/Core/transaction.c
2019-08-11 22:33:56 -07:00

273 lines
6.6 KiB
C

/*
* Copyright (c) 2009 Apple Inc. All rights reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
* This file contains Original Code and/or Modifications of Original Code
* as defined in and that are subject to the Apple Public Source License
* Version 2.0 (the 'License'). You may not use this file except in
* compliance with the License. Please obtain a copy of the License at
* http://www.opensource.apple.com/apsl/ and read it before using this
* file.
*
* The Original Code and all software distributed under the License are
* distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
* EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
* INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
* Please see the License for the specific language governing rights and
* limitations under the License.
*
* @APPLE_LICENSE_HEADER_END@
*/
#include <CoreFoundation/CoreFoundation.h>
#include <xpc/xpc.h>
#include <asl.h>
#include <opendirectory/odutils.h>
#include "CFODSession.h"
#include "CFODNode.h"
#include "internal.h"
#include "transaction.h"
#include <CFOpenDirectory/CFOpenDirectoryConstants.h>
#include "odxpc.h"
#include "rb.h"
struct od_transaction_s {
int32_t refcount;
uint32_t canceled;
CFDictionaryRef request;
ODSessionRef session;
ODNodeRef node;
dispatch_queue_t target_queue;
void *target_context;
transaction_callback_t target_callback;
};
#pragma mark Helper Functions
static bool
_attempt_recovery(od_transaction_t transaction, uint32_t error)
{
bool result = false;
switch (error) {
case kODErrorSessionInvalid:
break;
case kODErrorNodeInvalid:
if (!CFEqual(CFDictionaryGetValue(transaction->request, CFSTR("funcname")), CFSTR("ODNodeRelease"))) {
if (_ODNodeRecover(transaction->node)) {
result = true;
}
}
break;
case kODErrorQueryInvalid:
break;
case kODErrorContextInvalid:
break;
default:
break;
}
return result;
}
#pragma mark -
od_transaction_t
transaction_create(dispatch_queue_t queue, ODSessionRef session, ODNodeRef node, CFDictionaryRef request_dict, void *context, transaction_callback_t function)
{
od_transaction_t transaction;
transaction = calloc(1, sizeof(struct od_transaction_s));
transaction->refcount = 1;
transaction->session = session;
transaction->node = node;
transaction->request = CFRetain(request_dict);
dispatch_retain(queue);
transaction->target_queue = queue;
transaction->target_context = context;
transaction->target_callback = function;
return transaction;
}
static void
transaction_retain(od_transaction_t transaction)
{
if (__sync_add_and_fetch(&transaction->refcount, 1) == 1) {
OD_CRASH("resurrected a transaction object");
}
}
void
transaction_release(od_transaction_t transaction)
{
int32_t rc = __sync_sub_and_fetch(&transaction->refcount, 1);
if (rc > 0) {
return;
}
if (rc == 0) {
dispatch_release(transaction->target_queue);
CFRelease(transaction->request);
free(transaction);
return;
}
OD_CRASH("over-released a transaction object");
}
void
transaction_cancel(od_transaction_t transaction)
{
__sync_fetch_and_or(&transaction->canceled, 1);
}
static void
_handle_reply(od_transaction_t transaction, CFPropertyListRef plist, uint64_t error, bool complete)
{
if (_attempt_recovery(transaction, error)) {
transaction_send(transaction);
return;
}
transaction->target_callback(plist, error, complete, transaction->target_context);
}
void
transaction_send(od_transaction_t transaction)
{
uuid_t session_uuid, node_uuid;
CFDataRef data;
uuid_copy_session(session_uuid, transaction->session);
uuid_copy_node(node_uuid, transaction->node);
data = CFPropertyListCreateData(NULL, transaction->request, kCFPropertyListBinaryFormat_v1_0, 0, NULL);
if (data) {
transaction_retain(transaction);
odxpc_send_message_with_reply(0, session_uuid, node_uuid, data, transaction->target_queue, ^(CFPropertyListRef plist, uint64_t error, bool complete) {
if (!transaction->canceled) {
_handle_reply(transaction, plist, error, complete);
}
if (complete) {
transaction_release(transaction);
}
});
CFRelease(data);
} else {
transaction_retain(transaction);
dispatch_async(transaction->target_queue, ^{
if (!transaction->canceled) {
_handle_reply(transaction, NULL, kODErrorRecordParameterError, true);
}
transaction_release(transaction);
});
}
}
#pragma mark -
struct simple_ctx {
dispatch_semaphore_t semaphore;
CFArrayRef result;
uint32_t error_code;
};
static void
_simple_callback(CFDictionaryRef plist, uint32_t error, bool complete, void *context)
{
struct simple_ctx *ctx = (struct simple_ctx *)context;
assert(ctx->result == NULL);
schema_deconstruct_result(plist, NULL, &ctx->result);
ctx->error_code = error;
assert(complete);
dispatch_semaphore_signal(ctx->semaphore);
}
CFArrayRef
transaction_simple(uint32_t *code, ODSessionRef session, ODNodeRef node, CFStringRef funcname, CFIndex total_params, ...)
{
CFDictionaryRef request;
CFArrayRef result;
va_list ap;
od_transaction_t transaction;
va_start(ap, total_params);
request = schema_construct_requestv(funcname, total_params, ap);
va_end(ap);
if (request) {
dispatch_queue_t queue;
char qname[256];
struct simple_ctx ctx;
snprintf(qname, sizeof(qname), "com.apple.OpenDirectory.%s.%p.", __FUNCTION__, request);
CFStringGetCString(funcname, qname + strlen(qname), sizeof(qname) - strlen(qname), kCFStringEncodingUTF8);
queue = dispatch_queue_create(qname, NULL);
memset(&ctx, 0, sizeof(ctx));
ctx.semaphore = dispatch_semaphore_create(0);
transaction = transaction_create(queue, session, node, request, &ctx, _simple_callback);
transaction_send(transaction);
dispatch_semaphore_wait(ctx.semaphore, DISPATCH_TIME_FOREVER);
dispatch_release(ctx.semaphore);
transaction_cancel(transaction);
transaction_release(transaction);
dispatch_release(queue);
result = ctx.result;
*code = ctx.error_code;
CFRelease(request);
} else {
result = NULL;
*code = kODErrorRecordParameterError;
}
return result;
}
/*
* transaction_simple_response
* Checks various error conditions. In case of error, creates error. Only calls handler block if there is no error.
*/
void
transaction_simple_response(CFArrayRef response, uint32_t code, CFIndex error_index, CFErrorRef *error, transaction_response_handler_t handler)
{
CFTypeRef errInfo = NULL;
errInfo = response ? schema_get_value_at_index(response, error_index) : NULL;
if (code != 0) {
_ODErrorSet(error, code, errInfo);
} else {
if (error) {
*error = NULL;
}
handler();
}
if (response) {
CFRelease(response);
}
}