Initial import.

This code is based on original libxpc from NextBSD.
Improvements:
* MessagePack serialization instead of heavily-modified libnv
* Support for multiple transports (UNIX domain sockets and Mach IPC)
This commit is contained in:
Jakub Klama 2015-09-11 16:36:03 +02:00
parent 9072def42d
commit 404e5c1b70
28 changed files with 14504 additions and 0 deletions

66
CMakeLists.txt Normal file
View File

@ -0,0 +1,66 @@
#
# Copyright 2015 iXsystems, Inc.
# All rights reserved
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted providing that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
# OR SERVICES LOSS OF USE, DATA, OR PROFITS OR BUSINESS INTERRUPTION)
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
# IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
#
cmake_minimum_required(VERSION 3.2)
project(libxpc)
set(BASE_SOURCES
mpack.c
xpc_array.c
xpc_connection.c
xpc_dictionary.c
xpc_misc.c
xpc_type.c
)
set(HEADERS
activity.h
base.h
connection.h
debug.h
endpoint.h
launchd.h
xpc.h
)
set(UNIX_TRANSPORT_SOURCES
transports/unix.c
)
set(MACH_TRANSPORT_SOURCES
transports/mach.c
)
set(SOURCES
${BASE_SOURCES}
${UNIX_TRANSPORT_SOURCES}
)
include_directories(/usr/local/include)
set(CMAKE_INCLUDE_CURRENT_DIR ON)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fblocks")
add_library(libxpc SHARED ${SOURCES})
add_subdirectory(examples)

29
examples/CMakeLists.txt Normal file
View File

@ -0,0 +1,29 @@
#
# Copyright 2015 iXsystems, Inc.
# All rights reserved
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted providing that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
# OR SERVICES LOSS OF USE, DATA, OR PROFITS OR BUSINESS INTERRUPTION)
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
# IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
#
add_subdirectory(echo-client)
add_subdirectory(echo-server)
add_subdirectory(credentials)

View File

@ -0,0 +1,30 @@
#
# Copyright 2015 iXsystems, Inc.
# All rights reserved
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted providing that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
# OR SERVICES LOSS OF USE, DATA, OR PROFITS OR BUSINESS INTERRUPTION)
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
# IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
#
include_directories(../..)
link_directories(/usr/local/lib ../..)
add_executable(xpc-credentials-server xpc-credentials-server.c)
target_link_libraries(xpc-credentials-server BlocksRuntime dispatch sbuf xpc)

View File

@ -0,0 +1,63 @@
/*
* Copyright 2014-2015 iXsystems, Inc.
* All rights reserved
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted providing that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <dispatch/dispatch.h>
#include <xpc/xpc.h>
int
main(int argc, char *argv[])
{
xpc_connection_t conn;
if (argc < 2) {
fprintf(stderr, "Usage: %s <mach service name>\n", argv[0]);
return (1);
}
conn = xpc_connection_create_mach_service(argv[1], NULL,
XPC_CONNECTION_MACH_SERVICE_LISTENER);
xpc_connection_set_event_handler(conn, ^(xpc_object_t peer) {
xpc_connection_set_event_handler(peer, ^(xpc_object_t event) {
printf("Message received: %p\n", event);
printf("Client UID: %d\n", xpc_connection_get_euid(peer));
printf("Client GID: %d\n", xpc_connection_get_guid(peer));
printf("Client PID: %d\n", xpc_connection_get_pid(peer));
xpc_object_t resp = xpc_dictionary_create(NULL, NULL, 0);
xpc_dictionary_set_string(resp, "foo", "bar");
xpc_connection_send_message(peer, resp);
});
xpc_connection_resume(peer);
});
xpc_connection_resume(conn);
dispatch_main();
}

View File

@ -0,0 +1,30 @@
#
# Copyright 2015 iXsystems, Inc.
# All rights reserved
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted providing that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
# OR SERVICES LOSS OF USE, DATA, OR PROFITS OR BUSINESS INTERRUPTION)
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
# IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
#
include_directories(../..)
link_directories(/usr/local/lib ../..)
add_executable(xpc-echo-client xpc-echo-client.c)
target_link_libraries(xpc-echo-client BlocksRuntime dispatch sbuf xpc)

View File

@ -0,0 +1,84 @@
/*
* Copyright 2014-2015 iXsystems, Inc.
* All rights reserved
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted providing that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <xpc/xpc.h>
static void
connection_handler(xpc_connection_t peer)
{
xpc_connection_set_event_handler(peer, ^(xpc_object_t event) {
printf("Message received: %p\n", event);
});
xpc_connection_resume(peer);
}
int
main(int argc, char *argv[])
{
xpc_connection_t conn;
xpc_object_t msg;
msg = xpc_dictionary_create(NULL, NULL, 0);
xpc_dictionary_set_string(msg, "Hello", "world");
if (argc < 2) {
fprintf(stderr, "Usage: %s <mach service name>\n", argv[0]);
return (1);
}
conn = xpc_connection_create_mach_service(argv[1], NULL, 0);
if (conn == NULL) {
perror("xpc_connection_create_mach_service");
return (1);
}
xpc_connection_set_event_handler(conn, ^(xpc_object_t obj) {
printf("Received message in generic event handler: %p\n", obj);
printf("%s\n", xpc_copy_description(obj));
});
xpc_connection_resume(conn);
xpc_connection_send_message_with_reply(conn, msg, NULL, ^(xpc_object_t resp) {
printf("Received first message: %p\n", resp);
printf("%s\n", xpc_copy_description(resp));
});
xpc_connection_send_message_with_reply(conn, msg, NULL, ^(xpc_object_t resp) {
printf("Received second message: %p\n", resp);
printf("%s\n", xpc_copy_description(resp));
});
xpc_connection_send_message_with_reply(conn, msg, NULL, ^(xpc_object_t resp) {
printf("Received third message: %p\n", resp);
printf("%s\n", xpc_copy_description(resp));
});
dispatch_main();
}

View File

@ -0,0 +1,30 @@
#
# Copyright 2015 iXsystems, Inc.
# All rights reserved
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted providing that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
# OR SERVICES LOSS OF USE, DATA, OR PROFITS OR BUSINESS INTERRUPTION)
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
# IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
#
include_directories(../..)
link_directories(/usr/local/lib ../..)
add_executable(xpc-echo-server xpc-echo-server.c)
target_link_libraries(xpc-echo-server BlocksRuntime dispatch sbuf xpc)

View File

@ -0,0 +1,61 @@
/*
* Copyright 2014-2015 iXsystems, Inc.
* All rights reserved
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted providing that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <dispatch/dispatch.h>
#include <xpc/xpc.h>
int
main(int argc, char *argv[])
{
xpc_connection_t conn;
if (argc < 2) {
fprintf(stderr, "Usage: %s <mach service name>\n", argv[0]);
return (1);
}
conn = xpc_connection_create_mach_service(argv[1], NULL,
XPC_CONNECTION_MACH_SERVICE_LISTENER);
xpc_connection_set_event_handler(conn, ^(xpc_object_t peer) {
xpc_connection_set_event_handler(peer, ^(xpc_object_t event) {
printf("Message received: %p\n", event);
printf("%s\n", xpc_copy_description(event));
xpc_object_t resp = xpc_dictionary_create(NULL, NULL, 0);
xpc_dictionary_set_string(resp, "foo", "bar");
xpc_connection_send_message(peer, resp);
});
xpc_connection_resume(peer);
});
xpc_connection_resume(conn);
dispatch_main();
}

View File

@ -0,0 +1,13 @@
.include <src.opts.mk>
PROG= xpc-json-client
SRCS= xpc-json-client.c
BINDIR?= /usr/bin
NO_WERROR= yes
MK_MAN=no
CFLAGS+= -g -D__APPLE__ -fblocks
CFLAGS+= -I${.CURDIR}/../../../include/apple -I${.CURDIR}/../../../include
CFLAGS+= -I${.CURDIR}/../../../sys
LDADD+= -lmach -lBlocksRuntime -ldispatch -losxsupport -llaunch -lbsm -lnv -lsbuf -lxpc -pthread -ljansson
.include <bsd.prog.mk>

View File

@ -0,0 +1,162 @@
/*
* Copyright 2014-2015 iXsystems, Inc.
* All rights reserved
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted providing that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <jansson.h>
#include <xpc/xpc.h>
static xpc_object_t
to_xpc(json_t *json)
{
size_t idx;
xpc_object_t arr, dict;
const char *key;
json_t *val;
switch (json_typeof(json)) {
case JSON_STRING:
return xpc_string_create(json_string_value(json));
case JSON_INTEGER:
return xpc_int64_create(json_integer_value(json));
case JSON_TRUE:
return xpc_bool_create(true);
case JSON_FALSE:
return xpc_bool_create(false);
case JSON_ARRAY:
arr = xpc_array_create(NULL, 0);
json_array_foreach(json, idx, val) {
xpc_array_append_value(arr, to_xpc(val));
}
return arr;
case JSON_OBJECT:
dict = xpc_dictionary_create(NULL, NULL, 0);
json_object_foreach(json, key, val) {
xpc_dictionary_set_value(dict, key, to_xpc(val));
}
return dict;
default:
return xpc_null_create();
}
}
static json_t *
to_json(xpc_object_t xo)
{
char *txt;
json_t *arr, *obj;
xpc_type_t type;
size_t i;
type = xpc_get_type(xo);
printf("to_json: type=%p\n", type);
if (type == XPC_TYPE_STRING)
return json_string(xpc_string_get_string_ptr(xo));
if (type == XPC_TYPE_INT64)
return json_integer(xpc_int64_get_value(xo));
if (type == XPC_TYPE_UINT64)
return json_integer(xpc_uint64_get_value(xo));
if (type == XPC_TYPE_BOOL)
return json_boolean(xpc_bool_get_value(xo));
if (type == XPC_TYPE_ARRAY) {
arr = json_array();
xpc_array_apply(xo, ^(size_t index, xpc_object_t value) {
json_array_append_new(arr, to_json(value));
return ((bool)true);
});
return (arr);
}
if (type == XPC_TYPE_DICTIONARY) {
obj = json_object();
xpc_dictionary_apply(xo, ^(const char *key, xpc_object_t value) {
json_object_set_new(obj, key, to_json(value));
return ((bool)true);
});
return (obj);
}
return (json_null());
}
int
main(int argc, char *argv[])
{
xpc_connection_t conn;
xpc_object_t msg;
json_t *json;
json_error_t error;
if (argc < 2) {
fprintf(stderr, "Usage: %s <mach service name>\n", argv[0]);
return (1);
}
conn = xpc_connection_create_mach_service(argv[1], NULL, 0);
if (conn == NULL) {
perror("xpc_connection_create_mach_service");
return (1);
}
xpc_connection_set_event_handler(conn, ^(xpc_object_t obj) {
printf("Received message in generic event handler: %p\n", obj);
printf("%s\n", xpc_copy_description(obj));
});
xpc_connection_resume(conn);
json = json_loadf(stdin, JSON_DECODE_ANY, &error);
msg = to_xpc(json);
xpc_connection_send_message_with_reply(conn, msg, NULL, ^(xpc_object_t resp) {
printf("Received message: %p\n", resp);
json_t *reply;
reply = to_json(resp);
json_dumpf(reply, stdout, JSON_INDENT(4));
printf("\n");
});
dispatch_main();
return (0);
}

201
mpack-config.h Normal file
View File

@ -0,0 +1,201 @@
/**
* This is a sample MPack configuration file. Copy it to mpack-config.h somewhere
* in your project's include tree and, optionally, edit it to suit your setup.
* In most cases you can leave this file with the default config.
*/
#ifndef MPACK_CONFIG_H
#define MPACK_CONFIG_H 1
/*
* Features
*/
/** Enables compilation of the base Tag Reader. */
#define MPACK_READER 1
/** Enables compilation of the static Expect API. */
#define MPACK_EXPECT 1
/** Enables compilation of the dynamic Node API. */
#define MPACK_NODE 1
/** Enables compilation of the Writer. */
#define MPACK_WRITER 1
/*
* Dependencies
*/
/**
* Enables the use of C stdlib. This allows the library to use malloc
* for debugging and in allocation helpers.
*/
#define MPACK_STDLIB 1
/**
* Enables the use of C stdio. This adds helpers for easily
* reading/writing C files and makes debugging easier.
*/
#define MPACK_STDIO 1
/**
* \def MPACK_MALLOC
*
* Defines the memory allocation function used by mpack. This is used by
* helpers for automatically allocating data the correct size, and for
* debugging functions. If this macro is undefined, the allocation helpers
* will not be compiled.
*
* A memory allocator is required for the Node API.
*/
/**
* \def MPACK_REALLOC
*
* Defines the realloc function used by mpack. It is used by growable buffers
* to resize more quickly.
*
* This is optional, even when MPACK_MALLOC is used. If MPACK_MALLOC is
* set and MPACK_REALLOC is not, MPACK_MALLOC is used with a simple copy
* to grow buffers.
*/
#if defined(MPACK_STDLIB) && !defined(MPACK_MALLOC)
#define MPACK_MALLOC malloc
#define MPACK_REALLOC realloc
#endif
/**
* \def MPACK_FREE
*
* Defines the memory free function used by mpack. This is used by helpers
* for automatically allocating data the correct size. If this macro is
* undefined, the allocation helpers will not be compiled.
*
* A memory allocator is required for the Node API.
*/
#if defined(MPACK_STDLIB) && !defined(MPACK_FREE)
#define MPACK_FREE free
#endif
/**
* Enables the setjmp()/longjmp() error handling option. MPACK_MALLOC is required.
*
* Note that you don't have to use it; this just enables the option. It can be
* disabled to avoid the dependency on setjmp.h .
*/
#if defined(MPACK_MALLOC)
#define MPACK_SETJMP 1
#endif
/*
* Debugging options
*/
/**
* \def MPACK_DEBUG
*
* Enables debug features. You may want to wrap this around your
* own debug preprocs. By default, they are enabled if DEBUG or _DEBUG
* are defined.
*
* Note that MPACK_DEBUG cannot be defined differently for different
* source files because it affects layout of structs defined in header
* files. Your entire project must be compiled with the same value of
* MPACK_DEBUG. (This is why NDEBUG is not used.)
*/
#if defined(DEBUG) || defined(_DEBUG)
#define MPACK_DEBUG 1
#else
#define MPACK_DEBUG 0
#endif
/**
* Set this to 1 to implement a custom mpack_assert_fail() function. This
* function must not return, and must have the following signature:
*
* void mpack_assert_fail(const char* message)
*
* Asserts are only used when MPACK_DEBUG is enabled, and can be triggered
* by bugs in mpack or bugs due to incorrect usage of mpack.
*/
#define MPACK_CUSTOM_ASSERT 0
/**
* \def MPACK_READ_TRACKING
*
* Enables compound type size tracking for readers. This ensures that the
* correct number of elements or bytes are read from a compound type.
*
* This is enabled by default in debug builds (provided a malloc() is
* available.)
*/
#if MPACK_DEBUG && MPACK_READER && defined(MPACK_MALLOC)
#define MPACK_READ_TRACKING 1
#endif
/**
* \def MPACK_WRITE_TRACKING
*
* Enables compound type size tracking for writers. This ensures that the
* correct number of elements or bytes are written in a compound type.
*
* Note that without write tracking enabled, it is possible for buggy code
* to emit invalid MessagePack without flagging an error by writing the wrong
* number of elements or bytes in a compound type. With tracking enabled,
* MPACK will catch such errors and break on the offending line of code.
*
* This is enabled by default in debug builds (provided a malloc() is
* available.)
*/
#if MPACK_DEBUG && MPACK_WRITER && defined(MPACK_MALLOC)
#define MPACK_WRITE_TRACKING 1
#endif
/*
* Miscellaneous
*/
/**
* Stack space to use when initializing a reader or writer with a
* stack-allocated buffer.
*/
#define MPACK_STACK_SIZE 4096
/**
* Buffer size to use for allocated buffers (such as for a file writer.)
*/
#define MPACK_BUFFER_SIZE 65536
/**
* Number of nodes in each allocated node page.
*
* Nodes are 16 bytes when compiled for a 32-bit architecture and
* 24 bytes when compiled for a 64-bit architecture.
*
* Using as many nodes fit in one memory page seems to provide the
* best performance, and has very little waste when parsing small
* messages.
*/
#define MPACK_NODE_PAGE_SIZE (4096 / sizeof(mpack_node_t))
/**
* The initial depth for the node parser. When MPACK_MALLOC is available,
* the node parser has no practical depth limit, and it is not recursive
* so there is no risk of overflowing the call stack.
*/
#define MPACK_NODE_INITIAL_DEPTH 8
/**
* The maximum depth for the node parser if MPACK_MALLOC is not available.
* The parsing stack is placed on the call stack.
*/
#define MPACK_NODE_MAX_DEPTH_WITHOUT_MALLOC 32
#endif

3628
mpack.c Normal file

File diff suppressed because it is too large Load Diff

3567
mpack.h Normal file

File diff suppressed because it is too large Load Diff

134
transports/mach.c Normal file
View File

@ -0,0 +1,134 @@
/*
* Copyright 2014-2015 iXsystems, Inc.
* All rights reserved
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted providing that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <stdbool.h>
#include "../xpc_internal.h"
#define SOCKET_DIR "/var/run/xpc"
struct unix_pipe
{
char * up_path;
int up_socket;
bool up_listener;
};
static int
mach_lookup(const char *name, xpc_pipe_t *pipe)
{
if (flags & XPC_CONNECTION_MACH_SERVICE_LISTENER) {
kr = bootstrap_check_in(bootstrap_port, name,
&conn->xc_local_port);
if (kr != KERN_SUCCESS) {
errno = EBUSY;
free(conn);
return (NULL);
}
return (conn);
}
static int
mach_listen(const char *name, xpc_pipe_t *pipe)
{
kr = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE,
&conn->xc_local_port);
if (kr != KERN_SUCCESS) {
errno = EPERM;
return (NULL);
}
kr = mach_port_insert_right(mach_task_self(), conn->xc_local_port,
conn->xc_local_port, MACH_MSG_TYPE_MAKE_SEND);
if (kr != KERN_SUCCESS) {
errno = EPERM;
return (NULL);
}
}
static int
mach_send(xpc_pipe_t pipe, struct iovec *iov, struct xpc_resource *res, size_t nres)
{
}
static int
mach_recv(xpc_pipe_t pipe, struct iovec *iov, struct xpc_resource **res, size_t *nres)
{
struct xpc_recv_message message;
mach_msg_header_t *request;
kern_return_t kr;
mig_reply_error_t response;
mach_msg_trailer_t *tr;
int data_size;
struct xpc_object *xo;
audit_token_t *auditp;
xpc_u val;
request = &message.header;
/* should be size - but what about arbitrary XPC data? */
request->msgh_size = MAX_RECV;
request->msgh_local_port = local;
kr = mach_msg(request, MACH_RCV_MSG |
MACH_RCV_TRAILER_TYPE(MACH_MSG_TRAILER_FORMAT_0) |
MACH_RCV_TRAILER_ELEMENTS(MACH_RCV_TRAILER_AUDIT),
0, request->msgh_size, request->msgh_local_port,
MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
if (kr != 0)
LOG("mach_msg_receive returned %d\n", kr);
*remote = request->msgh_remote_port;
*id = message.id;
data_size = message.size;
tr = (mach_msg_trailer_t *)(((char *)&message) + request->msgh_size);
auditp = &((mach_msg_audit_trailer_t *)tr)->msgh_audit;
xo->xo_audit_token = malloc(sizeof(*auditp));
memcpy(xo->xo_audit_token, auditp, sizeof(*auditp));
xpc_dictionary_set_mach_send(xo, XPC_RPORT, request->msgh_remote_port);
xpc_dictionary_set_uint64(xo, XPC_SEQID, message.id);
*result = xo;
return (0);
}
static struct xpc_transport unix_transport = {
.listen = mach_listen,
.lookup = mach_lookup,
.get_credentials = mach_get_credentials,
.send = mach_send,
.recv = mach_recv
};

252
transports/unix.c Normal file
View File

@ -0,0 +1,252 @@
/*
* Copyright 2014-2015 iXsystems, Inc.
* All rights reserved
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted providing that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/stat.h>
#include <stdbool.h>
#include <dispatch/dispatch.h>
#include <xpc/xpc.h>
#include "../xpc_internal.h"
#define SOCKET_DIR "/var/run/xpc"
struct unix_port
{
int socket;
struct sockaddr_un sun;
};
static int
unix_lookup(const char *name, xpc_port_t *port)
{
struct sockaddr_un addr;
struct unix_port *ret;
struct stat st;
char *path;
asprintf(&path, "%s/%s", SOCKET_DIR, name);
if (stat(path, &st) != 0) {
return (-1);
}
if (!(st.st_mode & S_IFSOCK)) {
return (-1);
}
ret = malloc(sizeof(struct unix_port));
ret->socket = -1;
ret->sun.sun_family = AF_UNIX;
ret->sun.sun_len = sizeof(struct sockaddr_un);
strncpy(ret->sun.sun_path, path, sizeof(ret->sun.sun_path));
*port = ret;
return (0);
}
static int
unix_listen(const char *name, xpc_port_t *port)
{
struct sockaddr_un addr;
struct unix_port *ret;
struct stat st;
char *path;
int nb = 1;
if (name == NULL) {
asprintf(&path, "%s/anonymous.XXXXXXXX", SOCKET_DIR);
path = mktemp(path);
} else
asprintf(&path, "%s/%s", SOCKET_DIR, name);
unlink(path);
ret = malloc(sizeof(struct unix_port));
ret->sun.sun_family = AF_UNIX;
ret->sun.sun_len = sizeof(struct sockaddr_un);
strncpy(ret->sun.sun_path, path, sizeof(ret->sun.sun_path));
ret->socket = socket(AF_UNIX, SOCK_DGRAM, 0);
if (ret->socket == -1) {
free(ret);
return (-1);
}
if (bind(ret->socket, (struct sockaddr *)&ret->sun, sizeof(struct sockaddr_un)) != 0) {
debugf("bind failed: %s", strerror(errno));
free(ret);
return (-1);
}
//if (setsockopt(ret->socket, 0, LOCAL_CREDS, &nb, sizeof(nb)) == -1) {
// debugf("setsockopt failed: %s", strerror(errno));
// free(ret);
// return (-1);
//}
*port = ret;
return (0);
}
static int
unix_release(xpc_port_t port)
{
struct unix_port *uport = (struct unix_port *)port;
if (uport->socket != -1) {
close(uport->socket);
unlink(uport->sun.sun_path);
}
free(uport);
return (0);
}
static char *
unix_port_to_string(xpc_port_t port)
{
struct unix_port *uport = (struct unix_port *)port;
char *ret;
if (uport->socket != -1)
asprintf(&ret, "<%s [%d]>", uport->sun.sun_path, uport->socket);
else
asprintf(&ret, "<%s>", uport->sun.sun_path);
return (ret);
}
static int
unix_port_compare(xpc_port_t p1, xpc_port_t p2)
{
struct unix_port *up1 = (struct unix_port *)p1;
struct unix_port *up2 = (struct unix_port *)p2;
return (strncmp(up1->sun.sun_path, up2->sun.sun_path, sizeof up1->sun.sun_path));
}
static dispatch_source_t
unix_create_source(xpc_port_t port, dispatch_queue_t tq)
{
struct unix_port *uport = (struct unix_port *)port;
dispatch_source_t source;
return (dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, (uintptr_t)uport->socket, 0, tq));
}
static int
unix_send(xpc_port_t local, xpc_port_t remote, struct iovec *iov, int niov, struct xpc_resource *res, size_t nres)
{
struct unix_port *local_port = (struct unix_port *)local;
struct unix_port *remote_port = (struct unix_port *)remote;
struct msghdr msg;
union {
struct cmsghdr hdr;
struct cmsgcred cred;
} cmsg;
debugf("local=%s, remote=%s, msg=%p, size=%ld",
unix_port_to_string(local), unix_port_to_string(remote),
iov->iov_base, iov->iov_len);
msg.msg_name = &remote_port->sun;
msg.msg_namelen = remote_port->sun.sun_len;
msg.msg_iov = iov;
msg.msg_iovlen = niov;
msg.msg_control = (caddr_t) &cmsg;
msg.msg_controllen = CMSG_SPACE(sizeof(cmsg));
cmsg.hdr.cmsg_type = SCM_CREDS;
cmsg.hdr.cmsg_level = SOL_SOCKET;
cmsg.hdr.cmsg_len = CMSG_LEN(sizeof(struct cmsgcred));
if (sendmsg(local_port->socket, &msg, 0) < 0)
return (-1);
return (0);
}
static int
unix_recv(xpc_port_t local, xpc_port_t *remote, struct iovec *iov, int niov,
struct xpc_resource **res, size_t *nres, struct xpc_credentials *creds)
{
struct unix_port *port = (struct unix_port *)local;
struct unix_port *remote_port;
struct msghdr msg;
struct cmsgcred *recv_creds;
ssize_t recvd;
union {
struct cmsghdr hdr;
struct cmsgcred cred;
} cmsg;
remote_port = malloc(sizeof(struct unix_port));
remote_port->socket = -1;
msg.msg_name = &remote_port->sun;
msg.msg_namelen = sizeof(struct sockaddr_un);
msg.msg_iov = iov;
msg.msg_iovlen = niov;
msg.msg_control = (caddr_t) &cmsg;
msg.msg_controllen = CMSG_SPACE(sizeof(cmsg));
memset(&cmsg, 0, sizeof(cmsg));
recvd = recvmsg(port->socket, &msg, 0);
if (recvd < 0)
return (-1);
recv_creds = (struct cmsgcred *)CMSG_DATA(&cmsg);
creds->xc_remote_pid = recv_creds->cmcred_pid;
creds->xc_remote_euid = recv_creds->cmcred_euid;
creds->xc_remote_guid = recv_creds->cmcred_gid;
*remote = (xpc_port_t *)remote_port;
debugf("local=%s, remote=%s, msg=%p, len=%ld", unix_port_to_string(local), unix_port_to_string(*remote), iov->iov_base, recvd);
debugf("remote pid=%d, uid=%d, gid=%d", recv_creds->cmcred_pid, recv_creds->cmcred_uid, recv_creds->cmcred_gid);
return (0);
}
struct xpc_transport unix_transport = {
.xt_name = "unix",
.xt_listen = unix_listen,
.xt_lookup = unix_lookup,
.xt_release = unix_release,
.xt_port_to_string = unix_port_to_string,
.xt_port_compare = unix_port_compare,
.xt_create_source = unix_create_source,
.xt_send = unix_send,
.xt_recv = unix_recv
};

427
xpc/activity.h Normal file
View File

@ -0,0 +1,427 @@
#ifndef __XPC_ACTIVITY_H__
#define __XPC_ACTIVITY_H__
#ifndef __XPC_INDIRECT__
#error "Please #include <xpc/xpc.h> instead of this file directly."
// For HeaderDoc.
#include <xpc/base.h>
#endif // __XPC_INDIRECT__
#ifdef __BLOCKS__
__BEGIN_DECLS
/*
* The following are a collection of keys and values used to set an activity's
* execution criteria.
*/
/*!
* @constant XPC_ACTIVITY_INTERVAL
* An integer property indicating the desired time interval (in seconds) of the
* activity. The activity will not be run more than once per time interval.
* Due to the nature of XPC Activity finding an opportune time to run
* the activity, any two occurrences may be more or less than 'interval'
* seconds apart, but on average will be 'interval' seconds apart.
* The presence of this key implies the following, unless overridden:
* - XPC_ACTIVITY_REPEATING with a value of true
* - XPC_ACTIVITY_DELAY with a value of half the 'interval'
* The delay enforces a minimum distance between any two occurrences.
* - XPC_ACTIVITY_GRACE_PERIOD with a value of half the 'interval'.
* The grace period is the amount of time allowed to pass after the end of
* the interval before more aggressive scheduling occurs. The grace period
* does not increase the size of the interval.
*/
__OSX_AVAILABLE_STARTING(__MAC_10_9, __IPHONE_7_0)
XPC_EXPORT
const char *XPC_ACTIVITY_INTERVAL;
/*!
* @constant XPC_ACTIVITY_REPEATING
* A boolean property indicating whether this is a repeating activity.
*/
__OSX_AVAILABLE_STARTING(__MAC_10_9, __IPHONE_7_0)
XPC_EXPORT
const char *XPC_ACTIVITY_REPEATING;
/*!
* @constant XPC_ACTIVITY_DELAY
* An integer property indicating the number of seconds to delay before
* beginning the activity.
*/
__OSX_AVAILABLE_STARTING(__MAC_10_9, __IPHONE_7_0)
XPC_EXPORT
const char *XPC_ACTIVITY_DELAY;
/*!
* @constant XPC_ACTIVITY_GRACE_PERIOD
* An integer property indicating the number of seconds to allow as a grace
* period before the scheduling of the activity becomes more aggressive.
*/
__OSX_AVAILABLE_STARTING(__MAC_10_9, __IPHONE_7_0)
XPC_EXPORT
const char *XPC_ACTIVITY_GRACE_PERIOD;
__OSX_AVAILABLE_STARTING(__MAC_10_9, __IPHONE_7_0)
XPC_EXPORT
const int64_t XPC_ACTIVITY_INTERVAL_1_MIN;
__OSX_AVAILABLE_STARTING(__MAC_10_9, __IPHONE_7_0)
XPC_EXPORT
const int64_t XPC_ACTIVITY_INTERVAL_5_MIN;
__OSX_AVAILABLE_STARTING(__MAC_10_9, __IPHONE_7_0)
XPC_EXPORT
const int64_t XPC_ACTIVITY_INTERVAL_15_MIN;
__OSX_AVAILABLE_STARTING(__MAC_10_9, __IPHONE_7_0)
XPC_EXPORT
const int64_t XPC_ACTIVITY_INTERVAL_30_MIN;
__OSX_AVAILABLE_STARTING(__MAC_10_9, __IPHONE_7_0)
XPC_EXPORT
const int64_t XPC_ACTIVITY_INTERVAL_1_HOUR;
__OSX_AVAILABLE_STARTING(__MAC_10_9, __IPHONE_7_0)
XPC_EXPORT
const int64_t XPC_ACTIVITY_INTERVAL_4_HOURS;
__OSX_AVAILABLE_STARTING(__MAC_10_9, __IPHONE_7_0)
XPC_EXPORT
const int64_t XPC_ACTIVITY_INTERVAL_8_HOURS;
__OSX_AVAILABLE_STARTING(__MAC_10_9, __IPHONE_7_0)
XPC_EXPORT
const int64_t XPC_ACTIVITY_INTERVAL_1_DAY;
__OSX_AVAILABLE_STARTING(__MAC_10_9, __IPHONE_7_0)
XPC_EXPORT
const int64_t XPC_ACTIVITY_INTERVAL_7_DAYS;
/*!
* @constant XPC_ACTIVITY_PRIORITY
* A string property indicating the priority of the activity.
*/
__OSX_AVAILABLE_STARTING(__MAC_10_9, __IPHONE_7_0)
XPC_EXPORT
const char *XPC_ACTIVITY_PRIORITY;
/*!
* @constant XPC_ACTIVITY_PRIORITY_MAINTENANCE
* A string indicating activity is maintenance priority.
* Maintenance priority is intended for user-invisible maintenance tasks
* such as garbage collection or optimization.
*/
__OSX_AVAILABLE_STARTING(__MAC_10_9, __IPHONE_7_0)
XPC_EXPORT
const char *XPC_ACTIVITY_PRIORITY_MAINTENANCE;
/*!
* @constant XPC_ACTIVITY_PRIORITY_UTILITY
* A string indicating activity is utility priority.
* Utility priority is intended for user-visible tasks such as fetching data
* from the network, copying files, or importing data.
*/
__OSX_AVAILABLE_STARTING(__MAC_10_9, __IPHONE_7_0)
XPC_EXPORT
const char *XPC_ACTIVITY_PRIORITY_UTILITY;
/*!
* @constant XPC_ACTIVITY_ALLOW_BATTERY
* A Boolean value indicating whether the activity should be allowed to run
* while the computer is on battery power. The default value is false for
* maintenance priority activity and true for utility priority activity.
*/
__OSX_AVAILABLE_STARTING(__MAC_10_9, __IPHONE_7_0)
XPC_EXPORT
const char *XPC_ACTIVITY_ALLOW_BATTERY;
/*!
* @constant XPC_ACTIVITY_REQUIRE_SCREEN_SLEEP
* A Boolean value indicating whether the activity should only be performed
* while the primary screen is in sleep mode. Defaults to false.
*/
__OSX_AVAILABLE_STARTING(__MAC_10_9, __IPHONE_7_0)
XPC_EXPORT
const char *XPC_ACTIVITY_REQUIRE_SCREEN_SLEEP; // bool
/*!
* @constant XPC_ACTIVITY_REQUIRE_BATTERY_LEVEL
* An integer percentage of minimum battery charge required to allow the
* activity to run. A default minimum battery level is determined by the
* system.
*/
__OSX_AVAILABLE_STARTING(__MAC_10_9, __IPHONE_7_0)
XPC_EXPORT
const char *XPC_ACTIVITY_REQUIRE_BATTERY_LEVEL; // int (%)
/*!
* @constant XPC_ACTIVITY_REQUIRE_HDD_SPINNING
* A Boolean value indicating whether the activity should only be performed
* while the hard disk drive (HDD) is spinning. Computers with flash storage
* are considered to be equivalent to HDD spinning. Defaults to false.
*/
__OSX_AVAILABLE_STARTING(__MAC_10_9, __IPHONE_7_0)
XPC_EXPORT
const char *XPC_ACTIVITY_REQUIRE_HDD_SPINNING; // bool
/*!
* @define XPC_TYPE_ACTIVITY
* A type representing a connection to a named service. This connection is
* bidirectional and can be used to both send and receive messages. A
* connection carries the credentials of the remote service provider.
*/
#define XPC_TYPE_ACTIVITY (&_xpc_type_activity)
__OSX_AVAILABLE_STARTING(__MAC_10_9, __IPHONE_7_0)
XPC_EXPORT
XPC_TYPE(_xpc_type_activity);
/*!
* @typedef xpc_activity_t
*
* @abstract
* An XPC activity object.
*
* @discussion
* This object represents a set of execution criteria and a current execution
* state for background activity on the system. Once an activity is registered,
* the system will evaluate its criteria to determine whether the activity is
* eligible to run under current system conditions. When an activity becomes
* eligible to run, its execution state will be updated and an invocation of
* its handler block will be made.
*/
XPC_DECL(xpc_activity);
/*!
* @typedef xpc_activity_handler_t
*
* @abstract
* A block that is called when an XPC activity becomes eligible to run.
*/
typedef void (^xpc_activity_handler_t)(xpc_activity_t activity);
/*!
* @constant XPC_ACTIVITY_CHECK_IN
* This constant may be passed to xpc_activity_register() as the criteria
* dictionary in order to check in with the system for previously registered
* activity using the same identifier (for example, an activity taken from a
* launchd property list).
*/
__OSX_AVAILABLE_STARTING(__MAC_10_9, __IPHONE_7_0)
XPC_EXPORT
const xpc_object_t XPC_ACTIVITY_CHECK_IN;
/*!
* @function xpc_activity_register
*
* @abstract
* Registers an activity with the system.
*
* @discussion
* Registers a new activity with the system. The criteria of the activity are
* described by the dictionary passed to this function. If an activity with the
* same identifier already exists, the criteria provided override the existing
* criteria unless the special dictionary XPC_ACTIVITY_CHECK_IN is used. The
* XPC_ACTIVITY_CHECK_IN dictionary instructs the system to first look up an
* existing activity without modifying its criteria. Once the existing activity
* is found (or a new one is created with an empty set of criteria) the handler
* will be called with an activity object in the XPC_ACTIVITY_STATE_CHECK_IN
* state.
*
* @param identifier
* A unique identifier for the activity. Each application has its own namespace.
* The identifier should remain constant across registrations, relaunches of
* the application, and reboots. It should identify the kind of work being done,
* not a particular invocation of the work.
*
* @param criteria
* A dictionary of criteria for the activity.
*
* @param handler
* The handler block to be called when the activity changes state to one of the
* following states:
* - XPC_ACTIVITY_STATE_CHECK_IN (optional)
* - XPC_ACTIVITY_STATE_RUN
*
* The handler block is never invoked reentrantly. It will be invoked on a
* dispatch queue with an appropriate priority to perform the activity.
*/
__OSX_AVAILABLE_STARTING(__MAC_10_9, __IPHONE_7_0)
XPC_EXPORT XPC_NONNULL1 XPC_NONNULL2 XPC_NONNULL3
void
xpc_activity_register(const char *identifier, xpc_object_t criteria,
xpc_activity_handler_t handler);
/*!
* @function xpc_activity_copy_criteria
*
* @abstract
* Returns an XPC dictionary describing the execution criteria of an activity.
* This will return NULL in cases where the activity has already completed, e.g.
* when checking in to an event that finished and was not rescheduled.
*/
__OSX_AVAILABLE_STARTING(__MAC_10_9, __IPHONE_7_0)
XPC_EXPORT XPC_WARN_RESULT XPC_RETURNS_RETAINED
xpc_object_t
xpc_activity_copy_criteria(xpc_activity_t activity);
/*!
* @function xpc_activity_set_criteria
*
* @abstract
* Modifies the execution criteria of an activity.
*/
__OSX_AVAILABLE_STARTING(__MAC_10_9, __IPHONE_7_0)
XPC_EXPORT XPC_NONNULL1 XPC_NONNULL2
void
xpc_activity_set_criteria(xpc_activity_t activity, xpc_object_t criteria);
/*!
* @enum xpc_activity_state_t
* An activity is defined to be in one of the following states. Applications
* may check the current state of the activity using xpc_activity_get_state()
* in the handler block provided to xpc_activity_register().
*
* The application can modify the state of the activity by calling
* xpc_activity_set_state() with one of the following:
* - XPC_ACTIVITY_STATE_DEFER
* - XPC_ACTIVITY_STATE_CONTINUE
* - XPC_ACTIVITY_STATE_DONE
*
* @constant XPC_ACTIVITY_STATE_CHECK_IN
* An activity in this state has just completed a checkin with the system after
* XPC_ACTIVITY_CHECK_IN was provided as the criteria dictionary to
* xpc_activity_register. The state gives the application an opportunity to
* inspect and modify the activity's criteria.
*
* @constant XPC_ACTIVITY_STATE_WAIT
* An activity in this state is waiting for an opportunity to run. This value
* is never returned within the activity's handler block, as the block is
* invoked in response to XPC_ACTIVITY_STATE_CHECK_IN or XPC_ACTIVITY_STATE_RUN.
*
* Note:
* A launchd job may idle exit while an activity is in the wait state and be
* relaunched in response to the activity becoming runnable. The launchd job
* simply needs to re-register for the activity on its next launch by passing
* XPC_ACTIVITY_STATE_CHECK_IN to xpc_activity_register().
*
* @constant XPC_ACTIVITY_STATE_RUN
* An activity in this state is eligible to run based on its criteria.
*
* @constant XPC_ACTIVITY_STATE_DEFER
* An application may pass this value to xpc_activity_set_state() to indicate
* that the activity should be deferred (placed back into the WAIT state) until
* a time when its criteria are met again. Deferring an activity does not reset
* any of its time-based criteria (in other words, it will remain past due).
*
* IMPORTANT:
* This should be done in response to observing xpc_activity_should_defer().
* It should not be done unilaterally. If you determine that conditions are bad
* to do your activity's work for reasons you can't express in a criteria
* dictionary, you should set the activity's state to XPC_ACTIVITY_STATE_DONE.
*
*
* @constant XPC_ACTIVITY_STATE_CONTINUE
* An application may pass this value to xpc_activity_set_state() to indicate
* that the activity will continue its operation beyond the return of its
* handler block. This can be used to extend an activity to include asynchronous
* operations. The activity's handler block will not be invoked again until the
* state has been updated to either XPC_ACTIVITY_STATE_DEFER or, in the case
* of repeating activity, XPC_ACTIVITY_STATE_DONE.
*
* @constant XPC_ACTIVITY_STATE_DONE
* An application may pass this value to xpc_activity_set_state() to indicate
* that the activity has completed. For non-repeating activity, the resources
* associated with the activity will be automatically released upon return from
* the handler block. For repeating activity, timers present in the activity's
* criteria will be reset.
*/
enum {
XPC_ACTIVITY_STATE_CHECK_IN,
XPC_ACTIVITY_STATE_WAIT,
XPC_ACTIVITY_STATE_RUN,
XPC_ACTIVITY_STATE_DEFER,
XPC_ACTIVITY_STATE_CONTINUE,
XPC_ACTIVITY_STATE_DONE,
};
typedef long xpc_activity_state_t;
/*!
* @function xpc_activity_get_state
*
* @abstract
* Returns the current state of an activity.
*/
__OSX_AVAILABLE_STARTING(__MAC_10_9, __IPHONE_7_0)
XPC_EXPORT XPC_WARN_RESULT XPC_NONNULL1
xpc_activity_state_t
xpc_activity_get_state(xpc_activity_t activity);
/*!
* @function xpc_activity_set_state
*
* @abstract
* Updates the current state of an activity.
*
* @return
* Returns true if the state was successfully updated; otherwise, returns
* false if the requested state transition is not valid.
*/
__OSX_AVAILABLE_STARTING(__MAC_10_9, __IPHONE_7_0)
XPC_EXPORT XPC_WARN_RESULT XPC_NONNULL1
bool
xpc_activity_set_state(xpc_activity_t activity, xpc_activity_state_t state);
/*!
* @function xpc_activity_should_defer
*
* @abstract
* Test whether an activity should be deferred.
*
* @discussion
* This function may be used to test whether the criteria of a long-running
* activity are still satisfied. If not, the system indicates that the
* application should defer the activity. The application may acknowledge the
* deferral by calling xpc_activity_set_state() with XPC_ACTIVITY_STATE_DEFER.
* Once deferred, the system will place the activity back into the WAIT state
* and re-invoke the handler block at the earliest opportunity when the criteria
* are once again satisfied.
*
* @return
* Returns true if the activity should be deferred.
*/
__OSX_AVAILABLE_STARTING(__MAC_10_9, __IPHONE_7_0)
XPC_EXPORT
bool
xpc_activity_should_defer(xpc_activity_t activity);
/*!
* @function xpc_activity_unregister
*
* @abstract
* Unregisters an activity found by its identifier.
*
* @discussion
* A dynamically registered activity will be deleted in response to this call.
* Statically registered activity (from a launchd property list) will be
* reverted to its original criteria if any modifications were made.
*
* Unregistering an activity has no effect on any outstanding xpc_activity_t
* objects or any currently executing xpc_activity_handler_t blocks; however,
* no new handler block invocations will be made after it is unregistered.
*
* @param identifier
* The identifier of the activity to unregister.
*/
__OSX_AVAILABLE_STARTING(__MAC_10_9, __IPHONE_7_0)
XPC_EXPORT XPC_NONNULL1
void
xpc_activity_unregister(const char *identifier);
__END_DECLS
#endif // __BLOCKS__
#endif // __XPC_ACTIVITY_H__

167
xpc/base.h Normal file
View File

@ -0,0 +1,167 @@
// Copyright (c) 2009-2011 Apple Inc. All rights reserved.
#ifndef __XPC_BASE_H__
#define __XPC_BASE_H__
#include <sys/cdefs.h>
__BEGIN_DECLS
#if !defined(__has_include)
#define __has_include(x) 0
#endif // !defined(__has_include)
#if !defined(__has_attribute)
#define __has_attribute(x) 0
#endif // !defined(__has_attribute)
#if __has_include(<xpc/availability.h>)
#include <xpc/availability.h>
#else // __has_include(<xpc/availability.h>)
#ifdef __APPLE__
#include <Availability.h>
#endif
#define __XPC_IOS_SIMULATOR_AVAILABLE_STARTING(version)
#endif // __has_include(<xpc/availability.h>)
#if XPC_SERVICE_MAIN_IN_LIBXPC
#define XPC_HOSTING_OLD_MAIN 1
#else // XPC_SERVICE_MAIN_IN_LIBXPC
#define XPC_HOSTING_OLD_MAIN 0
#endif // XPC_SERVICE_MAIN_IN_LIBXPC
#ifndef __XPC_INDIRECT__
#error "Please #include <xpc/xpc.h> instead of this file directly."
#endif // __XPC_INDIRECT__
#pragma mark Attribute Shims
#ifdef __GNUC__
#define XPC_CONSTRUCTOR __attribute__((constructor))
#define XPC_NORETURN __attribute__((__noreturn__))
#define XPC_NOTHROW __attribute__((__nothrow__))
#define XPC_NONNULL1 __attribute__((__nonnull__(1)))
#define XPC_NONNULL2 __attribute__((__nonnull__(2)))
#define XPC_NONNULL3 __attribute__((__nonnull__(3)))
#define XPC_NONNULL4 __attribute__((__nonnull__(4)))
#define XPC_NONNULL5 __attribute__((__nonnull__(5)))
#define XPC_NONNULL6 __attribute__((__nonnull__(6)))
#define XPC_NONNULL7 __attribute__((__nonnull__(7)))
#define XPC_NONNULL8 __attribute__((__nonnull__(8)))
#define XPC_NONNULL9 __attribute__((__nonnull__(9)))
#define XPC_NONNULL10 __attribute__((__nonnull__(10)))
#define XPC_NONNULL11 __attribute__((__nonnull__(11)))
#define XPC_NONNULL_ALL __attribute__((__nonnull__))
#define XPC_SENTINEL __attribute__((__sentinel__))
#define XPC_PURE __attribute__((__pure__))
#define XPC_WARN_RESULT __attribute__((__warn_unused_result__))
#define XPC_MALLOC __attribute__((__malloc__))
#define XPC_UNUSED __attribute__((__unused__))
#define XPC_USED __attribute__((__used__))
#define XPC_PACKED __attribute__((__packed__))
#define XPC_PRINTF(m, n) __attribute__((format(printf, m, n)))
#define XPC_INLINE static __inline__ __attribute__((__always_inline__))
#define XPC_NOINLINE __attribute__((noinline))
#define XPC_NOIMPL __attribute__((unavailable))
#if __has_extension(attribute_unavailable_with_message)
#define XPC_UNAVAILABLE(m) __attribute__((unavailable(m)))
#else // __has_extension(attribute_unavailable_with_message)
#define XPC_UNAVAILABLE(m) XPC_NOIMPL
#endif // __has_extension(attribute_unavailable_with_message)
#define XPC_EXPORT extern __attribute__((visibility("default")))
#define XPC_NOEXPORT __attribute__((visibility("hidden")))
#define XPC_WEAKIMPORT extern __attribute__((weak_import))
#define XPC_DEBUGGER_EXCL XPC_NOEXPORT XPC_USED
#define XPC_TRANSPARENT_UNION __attribute__((transparent_union))
#if __clang__
#define XPC_DEPRECATED(m) __attribute__((deprecated(m)))
#else // __clang__
#define XPC_DEPRECATED(m) __attribute__((deprecated))
#endif // __clang
#if __has_feature(objc_arc)
#define XPC_GIVES_REFERENCE __strong
#define XPC_UNRETAINED __unsafe_unretained
#define XPC_BRIDGE(xo) ((__bridge void *)(xo))
#define XPC_BRIDGEREF_BEGIN(xo) ((__bridge_retained void *)(xo))
#define XPC_BRIDGEREF_BEGIN_WITH_REF(xo) ((__bridge void *)(xo))
#define XPC_BRIDGEREF_MIDDLE(xo) ((__bridge id)(xo))
#define XPC_BRIDGEREF_END(xo) ((__bridge_transfer id)(xo))
#else // __has_feature(objc_arc)
#define XPC_GIVES_REFERENCE
#define XPC_UNRETAINED
#define XPC_BRIDGE(xo)
#define XPC_BRIDGEREF_BEGIN(xo) (xo)
#define XPC_BRIDGEREF_BEGIN_WITH_REF(xo) (xo)
#define XPC_BRIDGEREF_MIDDLE(xo) (xo)
#define XPC_BRIDGEREF_END(xo) (xo)
#endif // __has_feature(objc_arc)
#define _xpc_unreachable() __builtin_unreachable()
#else // __GNUC__
/*! @parseOnly */
#define XPC_CONSTRUCTOR
/*! @parseOnly */
#define XPC_NORETURN
/*! @parseOnly */
#define XPC_NOTHROW
/*! @parseOnly */
#define XPC_NONNULL1
/*! @parseOnly */
#define XPC_NONNULL2
/*! @parseOnly */
#define XPC_NONNULL3
/*! @parseOnly */
#define XPC_NONNULL4
/*! @parseOnly */
#define XPC_NONNULL5
/*! @parseOnly */
#define XPC_NONNULL6
/*! @parseOnly */
#define XPC_NONNULL7
/*! @parseOnly */
#define XPC_NONNULL8
/*! @parseOnly */
#define XPC_NONNULL9
/*! @parseOnly */
#define XPC_NONNULL10
/*! @parseOnly */
#define XPC_NONNULL11
/*! @parseOnly */
#define XPC_NONNULL(n)
/*! @parseOnly */
#define XPC_NONNULL_ALL
/*! @parseOnly */
#define XPC_SENTINEL
/*! @parseOnly */
#define XPC_PURE
/*! @parseOnly */
#define XPC_WARN_RESULT
/*! @parseOnly */
#define XPC_MALLOC
/*! @parseOnly */
#define XPC_UNUSED
/*! @parseOnly */
#define XPC_PACKED
/*! @parseOnly */
#define XPC_PRINTF(m, n)
/*! @parseOnly */
#define XPC_INLINE static inline
/*! @parseOnly */
#define XPC_NOINLINE
/*! @parseOnly */
#define XPC_NOIMPL
/*! @parseOnly */
#define XPC_EXPORT extern
/*! @parseOnly */
#define XPC_WEAKIMPORT
/*! @parseOnly */
#define XPC_DEPRECATED
/*! @parseOnly */
#define XPC_UNAVAILABLE(m)
#endif // __GNUC__
__END_DECLS
#endif // __XPC_BASE_H__

714
xpc/connection.h Normal file
View File

@ -0,0 +1,714 @@
#ifndef __XPC_CONNECTION_H__
#define __XPC_CONNECTION_H__
#ifndef __XPC_INDIRECT__
#error "Please #include <xpc/xpc.h> instead of this file directly."
// For HeaderDoc.
#include <xpc/base.h>
#endif // __XPC_INDIRECT__
#ifndef __BLOCKS__
#error "XPC connections require Blocks support."
#endif // __BLOCKS__
__BEGIN_DECLS
/*!
* @constant XPC_ERROR_CONNECTION_INTERRUPTED
* Will be delivered to the connection's event handler if the remote service
* exited. The connection is still live even in this case, and resending a
* message will cause the service to be launched on-demand. This error serves
* as a client's indication that it should resynchronize any state that it had
* given the service.
*
* Any messages in the queue to be sent will be unwound and canceled when this
* error occurs. In the case where a message waiting to be sent has a reply
* handler, that handler will be invoked with this error. In the context of the
* reply handler, this error indicates that a reply to the message will never
* arrive.
*
* Messages that do not have reply handlers associated with them will be
* silently disposed of. This error will only be given to peer connections.
*/
#define XPC_ERROR_CONNECTION_INTERRUPTED \
XPC_GLOBAL_OBJECT(_xpc_error_connection_interrupted)
__OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_5_0)
XPC_EXPORT
const struct _xpc_dictionary_s _xpc_error_connection_interrupted;
/*!
* @constant XPC_ERROR_CONNECTION_INVALID
* Will be delivered to the connection's event handler if the named service
* provided to xpc_connection_create() could not be found in the XPC service
* namespace. The connection is useless and should be disposed of.
*
* Any messages in the queue to be sent will be unwound and canceled when this
* error occurs, similarly to the behavior when XPC_ERROR_CONNECTION_INTERRUPTED
* occurs. The only difference is that the XPC_ERROR_CONNECTION_INVALID will be
* given to outstanding reply handlers and the connection's event handler.
*
* This error may be given to any type of connection.
*/
#define XPC_ERROR_CONNECTION_INVALID \
XPC_GLOBAL_OBJECT(_xpc_error_connection_invalid)
__OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_5_0)
XPC_EXPORT
const struct _xpc_dictionary_s _xpc_error_connection_invalid;
/*!
* @constant XPC_ERROR_TERMINATION_IMMINENT
* This error will be delivered to a peer connection's event handler when the
* XPC runtime has determined that the program should exit and that all
* outstanding transactions must be wound down, and no new transactions can be
* opened.
*
* After this error has been delivered to the event handler, no more messages
* will be received by the connection. The runtime will still attempt to deliver
* outgoing messages, but this error should be treated as an indication that
* the program will exit very soon, and any outstanding business over the
* connection should be wrapped up as quickly as possible and the connection
* canceled shortly thereafter.
*
* This error will only be delivered to peer connections received through a
* listener or the xpc_main() event handler.
*/
#define XPC_ERROR_TERMINATION_IMMINENT \
XPC_GLOBAL_OBJECT(_xpc_error_termination_imminent)
__OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_5_0)
XPC_EXPORT
const struct _xpc_dictionary_s _xpc_error_termination_imminent;
/*!
* @constant XPC_CONNECTION_MACH_SERVICE_LISTENER
* Passed to xpc_connection_create_mach_service(). This flag indicates that the
* caller is the listener for the named service. This flag may only be passed
* for services which are advertised in the process' launchd.plist(5). You may
* not use this flag to dynamically add services to the Mach bootstrap
* namespace.
*/
#define XPC_CONNECTION_MACH_SERVICE_LISTENER (1 << 0)
/*!
* @constant XPC_CONNECTION_MACH_SERVICE_PRIVILEGED
* Passed to xpc_connection_create_mach_service(). This flag indicates that the
* job advertising the service name in its launchd.plist(5) should be in the
* privileged Mach bootstrap. This is typically accomplished by placing your
* launchd.plist(5) in /Library/LaunchDaemons. If specified alongside the
* XPC_CONNECTION_MACH_SERVICE_LISTENER flag, this flag is a no-op.
*/
#define XPC_CONNECTION_MACH_SERVICE_PRIVILEGED (1 << 1)
/*!
* @typedef xpc_finalizer_f
* A function that is invoked when a connection is being torn down and its
* context needs to be freed. The sole argument is the value that was given to
* {@link xpc_connection_set_context} or NULL if no context has been set. It is
* not safe to reference the connection from within this function.
*
* @param value
* The context object that is to be disposed of.
*/
typedef void (*xpc_finalizer_t)(void *value);
/*!
* @function xpc_connection_create
* Creates a new connection object.
*
* @param name
* If non-NULL, the name of the service with which to connect. The returned
* connection will be a peer.
*
* If NULL, an anonymous listener connection will be created. You can embed the
* ability to create new peer connections in an endpoint, which can be inserted
* into a message and sent to another process .
*
* @param targetq
* The GCD queue to which the event handler block will be submitted. This
* parameter may be NULL, in which case the connection's target queue will be
* libdispatch's default target queue, defined as DISPATCH_TARGET_QUEUE_DEFAULT.
* The target queue may be changed later with a call to
* xpc_connection_set_target_queue().
*
* @result
* A new connection object. The caller is responsible for disposing of the
* returned object with {@link xpc_release} when it is no longer needed.
*
* @discussion
* This method will succeed even if the named service does not exist. This is
* because the XPC namespace is not queried for the service name until
* the first call to xpc_connection_resume().
*
* XPC connections, like dispatch sources, are returned in a suspended state, so
* you must call {@link xpc_connection_resume()} in order to begin receiving
* events from the connection. Also like dispatch sources, connections must be
* resumed in order to be safely released. It is a programming error to release
* a suspended connection.
*/
__OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_5_0)
XPC_EXPORT XPC_MALLOC XPC_RETURNS_RETAINED XPC_WARN_RESULT
xpc_connection_t
xpc_connection_create(const char *name, dispatch_queue_t targetq);
/*!
* @function xpc_connection_create_mach_service
* Creates a new connection object representing a Mach service.
*
* @param name
* The name of the remote service with which to connect. The service name must
* exist in a Mach bootstrap that is accessible to the process and be advertised
* in a launchd.plist.
*
* @param targetq
* The GCD queue to which the event handler block will be submitted. This
* parameter may be NULL, in which case the connection's target queue will be
* libdispatch's default target queue, defined as DISPATCH_TARGET_QUEUE_DEFAULT.
* The target queue may be changed later with a call to
* xpc_connection_set_target_queue().
*
* @param flags
* Additional attributes with which to create the connection.
*
* @result
* A new connection object.
*
* @discussion
* If the XPC_CONNECTION_MACH_SERVICE_LISTENER flag is given to this method,
* then the connection returned will be a listener connection. Otherwise, a peer
* connection will be returned. See the documentation for
* {@link xpc_connection_set_event_handler()} for the semantics of listener
* connections versus peer connections.
*
* This method will succeed even if the named service does not exist. This is
* because the Mach namespace is not queried for the service name until the
* first call to {@link xpc_connection_resume()}.
*/
__OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_5_0)
XPC_EXPORT XPC_MALLOC XPC_RETURNS_RETAINED XPC_WARN_RESULT XPC_NONNULL1
xpc_connection_t
xpc_connection_create_mach_service(const char *name, dispatch_queue_t targetq,
uint64_t flags);
/*!
* @function xpc_connection_create_from_endpoint
* Creates a new connection from the given endpoint.
*
* @param endpoint
* The endpoint from which to create the new connection.
*
* @result
* A new peer connection to the listener represented by the given endpoint.
*
* The same responsibilities of setting an event handler and resuming the
* connection after calling xpc_connection_create() apply to the connection
* returned by this API. Since the connection yielded by this API is not
* associated with a name (and therefore is not rediscoverable), this connection
* will receive XPC_ERROR_CONNECTION_INVALID if the listening side crashes,
* exits or cancels the listener connection.
*/
__OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_5_0)
XPC_EXPORT XPC_MALLOC XPC_RETURNS_RETAINED XPC_WARN_RESULT XPC_NONNULL_ALL
xpc_connection_t
xpc_connection_create_from_endpoint(xpc_endpoint_t endpoint);
/*!
* @function xpc_connection_set_target_queue
* Sets the target queue of the given connection.
*
* @param connection
* The connection object which is to be manipulated.
*
* @param targetq
* The GCD queue to which the event handler block will be submitted. This
* parameter may be NULL, in which case the connection's target queue will be
* libdispatch's default target queue, defined as DISPATCH_TARGET_QUEUE_DEFAULT.
*
* @discussion
* Setting the target queue is asynchronous and non-preemptive and therefore
* this method will not interrupt the execution of an already-running event
* handler block. Setting the target queue may be likened to issuing a barrier
* to the connection which does the actual work of changing the target queue.
*
* The XPC runtime guarantees this non-preemptiveness even for concurrent target
* queues. If the target queue is a concurrent queue, then XPC still guarantees
* that there will never be more than one invocation of the connection's event
* handler block executing concurrently. If you wish to process events
* concurrently, you can dispatch_async(3) to a concurrent queue from within
* the event handler.
*
* IMPORTANT: When called from within the event handler block,
* dispatch_get_current_queue(3) is NOT guaranteed to return a pointer to the
* queue set with this method.
*
* Despite this seeming inconsistency, the XPC runtime guarantees that, when the
* target queue is a serial queue, the event handler block will execute
* synchonously with respect to other blocks submitted to that same queue. When
* the target queue is a concurrent queue, the event handler block may run
* concurrently with other blocks submitted to that queue, but it will never run
* concurrently with other invocations of itself for the same connection, as
* discussed previously.
*/
__OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_5_0)
XPC_EXPORT XPC_NONNULL1
void
xpc_connection_set_target_queue(xpc_connection_t connection,
dispatch_queue_t targetq);
/*!
* @function xpc_connection_set_event_handler
* Sets the event handler block for the connection.
*
* @param connection
* The connection object which is to be manipulated.
*
* @param handler
* The event handler block.
*
* @discussion
* Setting the event handler is asynchronous and non-preemptive, and therefore
* this method will not interrupt the execution of an already-running event
* handler block. If the event handler is executing at the time of this call, it
* will finish, and then the connection's event handler will be changed before
* the next invocation of the event handler. The XPC runtime guarantees this
* non-preemptiveness even for concurrent target queues.
*
* Connection event handlers are non-reentrant, so it is safe to call
* xpc_connection_set_event_handler() from within the event handler block.
*
* The event handler's execution should be treated as a barrier to all
* connection activity. When it is executing, the connection will not attempt to
* send or receive messages, including reply messages. Thus, it is not safe to
* call xpc_connection_send_message_with_reply_sync() on the connection from
* within the event handler.
*
* You do not hold a reference on the object received as the event handler's
* only argument. Regardless of the type of object received, it is safe to call
* xpc_retain() on the object to obtain a reference to it.
*
* A connection may receive different events depending upon whether it is a
* listener or not. Any connection may receive an error in its event handler.
* But while normal connections may receive messages in addition to errors,
* listener connections will receive connections and and not messages.
*
* Connections received by listeners are equivalent to those returned by
* xpc_connection_create() with a non-NULL name argument and a NULL targetq
* argument with the exception that you do not hold a reference on them.
* You must set an event handler and resume the connection. If you do not wish
* to accept the connection, you may simply call xpc_connection_cancel() on it
* and return. The runtime will dispose of it for you.
*
* If there is an error in the connection, this handler will be invoked with the
* error dictionary as its argument. This dictionary will be one of the well-
* known XPC_ERROR_* dictionaries.
*
* Regardless of the type of event, ownership of the event object is NOT
* implicitly transferred. Thus, the object will be released and deallocated at
* some point in the future after the event handler returns. If you wish the
* event's lifetime to persist, you must retain it with xpc_retain().
*
* Connections received through the event handler will be released and
* deallocated after the connection has gone invalid and delivered that event to
* its event handler.
*/
__OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_5_0)
XPC_EXPORT XPC_NONNULL_ALL
void
xpc_connection_set_event_handler(xpc_connection_t connection,
xpc_handler_t handler);
/*!
* @function xpc_connection_suspend
* Suspends the connection so that the event handler block will not fire and
* that the connection will not attempt to send any messages it has in its
* queue. All calls to xpc_connection_suspend() must be balanced with calls to
* xpc_connection_resume() before releasing the last reference to the
* connection.
*
* @param connection
* The connection object which is to be manipulated.
*
* @discussion
* Suspension is asynchronous and non-preemptive, and therefore this method will
* not interrupt the execution of an already-running event handler block. If
* the event handler is executing at the time of this call, it will finish, and
* then the connection will be suspended before the next scheduled invocation
* of the event handler. The XPC runtime guarantees this non-preemptiveness even
* for concurrent target queues.
*
* Connection event handlers are non-reentrant, so it is safe to call
* xpc_connection_suspend() from within the event handler block.
*/
__OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_5_0)
XPC_EXPORT XPC_NONNULL_ALL
void
xpc_connection_suspend(xpc_connection_t connection);
/*!
* @function xpc_connection_resume
* Resumes the connection. Connections start in a suspended state, so you must
* call xpc_connection_resume() on a connection before it will send or receive
* any messages.
*
* @param connection
* The connection object which is to be manipulated.
*
* @discussion
* In order for a connection to become live, every call to
* xpc_connection_suspend() must be balanced with a call to
* xpc_connection_resume() after the initial call to xpc_connection_resume().
* After the initial resume of the connection, calling xpc_connection_resume()
* more times than xpc_connection_suspend() has been called is considered an
* error.
*/
__OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_5_0)
XPC_EXPORT XPC_NONNULL_ALL
void
xpc_connection_resume(xpc_connection_t connection);
/*!
* @function xpc_connection_send_message
* Sends a message over the connection to the destination service.
*
* @param connection
* The connection over which the message shall be sent.
*
* @param message
* The message to send. This must be a dictionary object. This dictionary is
* logically copied by the connection, so it is safe to modify the dictionary
* after this call.
*
* @discussion
* Messages are delivered in FIFO order. This API is safe to call from multiple
* GCD queues. There is no indication that a message was delivered successfully.
* This is because even once the message has been successfully enqueued on the
* remote end, there are no guarantees about when the runtime will dequeue the
* message and invoke the other connection's event handler block.
*
* If this API is used to send a message that is in reply to another message,
* there is no guarantee of ordering between the invocations of the connection's
* event handler and the reply handler for that message, even if they are
* targeted to the same queue.
*
* After extensive study, we have found that clients who are interested in
* the state of the message on the server end are typically holding open
* transactions related to that message. And the only reliable way to track the
* lifetime of that transaction is at the protocol layer. So the server should
* send a reply message, which upon receiving, will cause the client to close
* its transaction.
*/
__OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_5_0)
XPC_EXPORT XPC_NONNULL_ALL
void
xpc_connection_send_message(xpc_connection_t connection, xpc_object_t message);
/*!
* @function xpc_connection_send_barrier
* Issues a barrier against the connection's message-send activity.
*
* @param connection
* The connection against which the barrier is to be issued.
*
* @param barrier
* The barrier block to issue. This barrier prevents concurrent message-send
* activity on the connection. No messages will be sent while the barrier block
* is executing.
*
* @discussion
* XPC guarantees that, even if the connection's target queue is a concurrent
* queue, there are no other messages being sent concurrently while the barrier
* block is executing. XPC does not guarantee that the reciept of messages
* (either through the connection's event handler or through reply handlers)
* will be suspended while the barrier is executing.
*
* A barrier is issued relative to the message-send queue. Thus, if you call
* xpc_connection_send_message() five times and then call
* xpc_connection_send_barrier(), the barrier will be invoked after the fifth
* message has been sent and its memory disposed of. You may safely cancel a
* connection from within a barrier block.
*
* If a barrier is issued after sending a message which expects a reply, the
* behavior is the same as described above. The receipt of a reply message will
* not influence when the barrier runs.
*
* A barrier block can be useful for throttling resource consumption on the
* connected side of a connection. For example, if your connection sends many
* large messages, you can use a barrier to limit the number of messages that
* are inflight at any given time. This can be particularly useful for messages
* that contain kernel resources (like file descriptors) which have a system-
* wide limit.
*
* If a barrier is issued on a canceled connection, it will be invoked
* immediately. If a connection has been canceled and still has outstanding
* barriers, those barriers will be invoked as part of the connection's
* unwinding process.
*
* It is important to note that a barrier block's execution order is not
* guaranteed with respect to other blocks that have been scheduled on the
* target queue of the connection. Or said differently,
* xpc_connection_send_barrier(3) is not equivalent to dispatch_async(3).
*/
__OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_5_0)
XPC_EXPORT XPC_NONNULL_ALL
void
xpc_connection_send_barrier(xpc_connection_t connection,
dispatch_block_t barrier);
/*!
* @function xpc_connection_send_message_with_reply
* Sends a message over the connection to the destination service and associates
* a handler to be invoked when the remote service sends a reply message.
*
* @param connection
* The connection over which the message shall be sent.
*
* @param message
* The message to send. This must be a dictionary object.
*
* @param replyq
* The GCD queue to which the reply handler will be submitted. This may be a
* concurrent queue.
*
* @param handler
* The handler block to invoke when a reply to the message is received from
* the connection. If the remote service exits prematurely before the reply was
* received, the XPC_ERROR_CONNECTION_INTERRUPTED error will be returned.
* If the connection went invalid before the message could be sent, the
* XPC_ERROR_CONNECTION_INVALID error will be returned.
*
* @discussion
* If the given GCD queue is a concurrent queue, XPC cannot guarantee that there
* will not be multiple reply handlers being invoked concurrently. XPC does not
* guarantee any ordering for the invocation of reply handers. So if multiple
* messages are waiting for replies and the connection goes invalid, there is no
* guarantee that the reply handlers will be invoked in FIFO order. Similarly,
* XPC does not guarantee that reply handlers will not run concurrently with
* the connection's event handler in the case that the reply queue and the
* connection's target queue are the same concurrent queue.
*/
__OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_5_0)
XPC_EXPORT XPC_NONNULL1 XPC_NONNULL2 XPC_NONNULL4
void
xpc_connection_send_message_with_reply(xpc_connection_t connection,
xpc_object_t message, dispatch_queue_t replyq, xpc_handler_t handler);
/*!
* @function xpc_connection_send_message_with_reply_sync
* Sends a message over the connection and blocks the caller until a reply is
* received.
*
* @param connection
* The connection over which the message shall be sent.
*
* @param message
* The message to send. This must be a dictionary object.
*
* @result
* The message that the remote service sent in reply to the original message.
* If the remote service exits prematurely before the reply was received, the
* XPC_ERROR_CONNECTION_INTERRUPTED error will be returned. If the connection
* went invalid before the message could be sent, the
* XPC_ERROR_CONNECTION_INVALID error will be returned.
*
* You are responsible for releasing the returned object.
*
* @discussion
* This API is primarily for transitional purposes. Its implementation is
* conceptually equivalent to calling xpc_connection_send_message_with_reply()
* and then immediately blocking the calling thread on a semaphore and
* signaling the semaphore from the reply block.
*
* Be judicious about your use of this API. It can block indefinitely, so if you
* are using it to implement an API that can be called from the main thread, you
* may wish to consider allowing the API to take a queue and callback block so
* that results may be delivered asynchronously if possible.
*/
__OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_5_0)
XPC_EXPORT XPC_NONNULL_ALL XPC_WARN_RESULT XPC_RETURNS_RETAINED
xpc_object_t
xpc_connection_send_message_with_reply_sync(xpc_connection_t connection,
xpc_object_t message);
/*!
* @function xpc_connection_cancel
* Cancels the connection and ensures that its event handler will not fire
* again. After this call, any messages that have not yet been sent will be
* discarded, and the connection will be unwound. If there are messages that are
* awaiting replies, they will have their reply handlers invoked with the
* XPC_ERROR_CONNECTION_INVALID error.
*
* @param connection
* The connection object which is to be manipulated.
*
* @discussion
* Cancellation is asynchronous and non-preemptive and therefore this method
* will not interrupt the execution of an already-running event handler block.
* If the event handler is executing at the time of this call, it will finish,
* and then the connection will be canceled, causing a final invocation of the
* event handler to be scheduled with the XPC_ERROR_CONNECTION_INVALID error.
* After that invocation, there will be no further invocations of the event
* handler.
*
* The XPC runtime guarantees this non-preemptiveness even for concurrent target
* queues.
*/
__OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_5_0)
XPC_EXPORT XPC_NONNULL_ALL
void
xpc_connection_cancel(xpc_connection_t connection);
/*!
* @function xpc_connection_get_name
* Returns the name of the service with which the connections was created.
*
* @param connection
* The connection object which is to be examined.
*
* @result
* The name of the remote service. If you obtained the connection through an
* invocation of another connection's event handler, NULL is returned.
*/
__OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_5_0)
XPC_EXPORT XPC_NONNULL_ALL XPC_WARN_RESULT
const char *
xpc_connection_get_name(xpc_connection_t connection);
/*!
* @function xpc_connection_get_euid
* Returns the EUID of the remote peer.
*
* @param connection
* The connection object which is to be examined.
*
* @result
* The EUID of the remote peer at the time the connection was made.
*/
__OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_5_0)
XPC_EXPORT XPC_NONNULL_ALL XPC_WARN_RESULT
uid_t
xpc_connection_get_euid(xpc_connection_t connection);
/*!
* @function xpc_connection_get_egid
* Returns the EGID of the remote peer.
*
* @param connection
* The connection object which is to be examined.
*
* @result
* The EGID of the remote peer at the time the connection was made.
*/
__OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_5_0)
XPC_EXPORT XPC_NONNULL_ALL XPC_WARN_RESULT
gid_t
xpc_connection_get_egid(xpc_connection_t connection);
/*!
* @function xpc_connection_get_pid
* Returns the PID of the remote peer.
*
* @param connection
* The connection object which is to be examined.
*
* @result
* The PID of the remote peer.
*
* @discussion
* A given PID is not guaranteed to be unique across an entire boot cycle.
* Great care should be taken when dealing with this information, as it can go
* stale after the connection is established. OS X recycles PIDs, and therefore
* another process could spawn and claim the PID before a message is actually
* received from the connection.
*
* XPC will deliver an error to your event handler if the remote process goes
* away, but there are no guarantees as to the timing of this notification's
* delivery either at the kernel layer or at the XPC layer.
*/
__OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_5_0)
XPC_EXPORT XPC_NONNULL_ALL XPC_WARN_RESULT
pid_t
xpc_connection_get_pid(xpc_connection_t connection);
/*!
* @function xpc_connection_get_asid
* Returns the audit session identifier of the remote peer.
*
* @param connection
* The connection object which is to be examined.
*
* @result
* The audit session ID of the remote peer at the time the connection was made.
*/
__OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_5_0)
XPC_EXPORT XPC_NONNULL_ALL XPC_WARN_RESULT
au_asid_t
xpc_connection_get_asid(xpc_connection_t connection);
/*!
* @function xpc_connection_set_context
* Sets context on an connection.
*
* @param connection
* The connection which is to be manipulated.
*
* @param context
* The context to associate with the connection.
*
* @discussion
* If you must manage the memory of the context object, you must set a finalizer
* to dispose of it. If this method is called on a connection which already has
* context associated with it, the finalizer will NOT be invoked. The finalizer
* is only invoked when the connection is being deallocated.
*
* It is recommended that, instead of changing the actual context pointer
* associated with the object, you instead change the state of the context
* object itself.
*/
__OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_5_0)
XPC_EXPORT XPC_NONNULL1
void
xpc_connection_set_context(xpc_connection_t connection, void *context);
/*!
* @function xpc_connection_get_context
* Returns the context associated with the connection.
*
* @param connection
* The connection which is to be examined.
*
* @result
* The context associated with the connection. NULL if there has been no context
* associated with the object.
*/
__OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_5_0)
XPC_EXPORT XPC_NONNULL_ALL XPC_WARN_RESULT
void *
xpc_connection_get_context(xpc_connection_t connection);
/*!
* @function xpc_connection_set_finalizer_f
* Sets the finalizer for the given connection.
*
* @param connection
* The connection on which to set the finalizer.
*
* @param finalizer
* The function that will be invoked when the connection's retain count has
* dropped to zero and is being torn down.
*
* @discussion
* This method disposes of the context value associated with a connection, as
* set by {@link xpc_connection_set_context}.
*
* For many uses of context objects, this API allows for a convenient shorthand
* for freeing them. For example, for a context object allocated with malloc(3):
*
* xpc_connection_set_finalizer_f(object, free);
*/
__OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_5_0)
XPC_EXPORT XPC_NONNULL1
void
xpc_connection_set_finalizer_f(xpc_connection_t connection,
xpc_finalizer_t finalizer);
__END_DECLS
#endif // __XPC_CONNECTION_H__

23
xpc/debug.h Normal file
View File

@ -0,0 +1,23 @@
#ifndef __XPC_DEBUG_H__
#define __XPC_DEBUG_H__
/*!
* @function xpc_debugger_api_misuse_info
* Returns a pointer to a string describing the reason XPC aborted the calling
* process. On OS X, this will be the same string present in the "Application
* Specific Information" section of the crash report.
*
* @result
* A pointer to the human-readable string describing the reason the caller was
* aborted. If XPC was not responsible for the program's termination, NULL will
* be returned.
*
* @discussion
* This function is only callable from within a debugger. It is not meant to be
* called by the program directly.
*/
XPC_DEBUGGER_EXCL
const char *
xpc_debugger_api_misuse_info(void);
#endif // __XPC_DEBUG_H__

22
xpc/endpoint.h Normal file
View File

@ -0,0 +1,22 @@
#ifndef __XPC_ENDPOINT_H__
#define __XPC_ENDPOINT_H__
/*!
* @function xpc_endpoint_create
* Creates a new endpoint from a connection that is suitable for embedding into
* messages.
*
* @param connection
* Only connections obtained through calls to xpc_connection_create*() may be
* given to this API. Passing any other type of connection is not supported and
* will result in undefined behavior.
*
* @result
* A new endpoint object.
*/
__OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_5_0)
XPC_EXPORT XPC_MALLOC XPC_RETURNS_RETAINED XPC_WARN_RESULT XPC_NONNULL1
xpc_endpoint_t
xpc_endpoint_create(xpc_connection_t connection);
#endif // __XPC_ENDPOINT_H__

24
xpc/launchd.h Normal file
View File

@ -0,0 +1,24 @@
#ifndef XPC_LAUNCHD_H_
#define XPC_LAUNCHD_H_
#include <xpc/xpc.h>
#define EXNOERROR 0
#define EXNOMEM 1
#define EXINVAL 2
#define EXSRCH 3
#define EXMAX EXSRCH
const char *xpc_strerror(int error);
xpc_object_t xpc_copy_entitlement_for_token(const char *, audit_token_t *);
int xpc_pipe_routine_reply(xpc_object_t);
int xpc_pipe_try_receive(mach_port_t, xpc_object_t *, mach_port_t *,
boolean_t (*)(mach_msg_header_t *, mach_msg_header_t *), mach_msg_size_t, int);
kern_return_t xpc_call_wakeup(mach_port_t, int);
void xpc_dictionary_get_audit_token(xpc_object_t, audit_token_t *);
void xpc_dictionary_set_mach_recv(xpc_object_t, const char *, mach_port_t);
void xpc_dictionary_set_mach_send(xpc_object_t, const char *, mach_port_t);
mach_port_t xpc_dictionary_copy_mach_send(xpc_object_t, const char *);
xpc_object_t xpc_copy_entitlements_for_pid(pid_t);
xpc_object_t ld2xpc(launch_data_t);
#endif

2537
xpc/xpc.h Normal file

File diff suppressed because it is too large Load Diff

324
xpc_array.c Normal file
View File

@ -0,0 +1,324 @@
/*
* Copyright 2014-2015 iXsystems, Inc.
* All rights reserved
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted providing that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*/
#include <sys/types.h>
#include <xpc/xpc.h>
#include "xpc_internal.h"
xpc_object_t
xpc_array_create(const xpc_object_t *objects, size_t count)
{
struct xpc_object *xo;
size_t i;
xpc_u val;
xo = _xpc_prim_create(_XPC_TYPE_ARRAY, val, 0);
for (i = 0; i < count; i++)
xpc_array_append_value(xo, objects[i]);
return (xo);
}
void
xpc_array_set_value(xpc_object_t xarray, size_t index, xpc_object_t value)
{
struct xpc_object *xo, *xotmp, *xotmp2;
struct xpc_array_head *arr;
size_t i;
xo = xarray;
arr = &xo->xo_array;
i = 0;
if (index == XPC_ARRAY_APPEND)
return xpc_array_append_value(xarray, value);
if (index >= (size_t)xo->xo_size)
return;
TAILQ_FOREACH_SAFE(xotmp, arr, xo_link, xotmp2) {
if (i++ == index) {
TAILQ_INSERT_AFTER(arr, (struct xpc_object *)value,
xotmp, xo_link);
TAILQ_REMOVE(arr, xotmp, xo_link);
xpc_retain(value);
free(xotmp);
break;
}
}
}
void
xpc_array_append_value(xpc_object_t xarray, xpc_object_t value)
{
struct xpc_object *xo;
struct xpc_array_head *arr;
xo = xarray;
arr = &xo->xo_array;
TAILQ_INSERT_TAIL(arr, (struct xpc_object *)value, xo_link);
xpc_retain(value);
}
xpc_object_t
xpc_array_get_value(xpc_object_t xarray, size_t index)
{
struct xpc_object *xo, *xotmp;
struct xpc_array_head *arr;
size_t i;
xo = xarray;
arr = &xo->xo_array;
i = 0;
if (index > xo->xo_size)
return (NULL);
TAILQ_FOREACH(xotmp, arr, xo_link) {
if (i++ == index)
return (xotmp);
}
return (NULL);
}
size_t
xpc_array_get_count(xpc_object_t xarray)
{
struct xpc_object *xo;
xo = xarray;
return (xo->xo_size);
}
void
xpc_array_set_bool(xpc_object_t xarray, size_t index, bool value)
{
struct xpc_object *xo, *xotmp;
xo = xarray;
xotmp = xpc_bool_create(value);
return (xpc_array_set_value(xarray, index, xotmp));
}
void
xpc_array_set_int64(xpc_object_t xarray, size_t index, int64_t value)
{
struct xpc_object *xo, *xotmp;
xo = xarray;
xotmp = xpc_int64_create(value);
return (xpc_array_set_value(xarray, index, xotmp));
}
void
xpc_array_set_uint64(xpc_object_t xarray, size_t index, uint64_t value)
{
struct xpc_object *xo, *xotmp;
xo = xarray;
xotmp = xpc_uint64_create(value);
return (xpc_array_set_value(xarray, index, xotmp));
}
void
xpc_array_set_double(xpc_object_t xarray, size_t index, double value)
{
struct xpc_object *xo, *xotmp;
xo = xarray;
xotmp = xpc_double_create(value);
return (xpc_array_set_value(xarray, index, xotmp));
}
void
xpc_array_set_date(xpc_object_t xarray, size_t index, int64_t value)
{
struct xpc_object *xo, *xotmp;
xo = xarray;
xotmp = xpc_date_create(value);
return (xpc_array_set_value(xarray, index, xotmp));
}
void
xpc_array_set_data(xpc_object_t xarray, size_t index, const void *data,
size_t length)
{
struct xpc_object *xo, *xotmp;
xo = xarray;
xotmp = xpc_data_create(data, length);
return (xpc_array_set_value(xarray, index, xotmp));
}
void
xpc_array_set_string(xpc_object_t xarray, size_t index, const char *string)
{
struct xpc_object *xo, *xotmp;
xo = xarray;
xotmp = xpc_string_create(string);
return (xpc_array_set_value(xarray, index, xotmp));
}
void
xpc_array_set_uuid(xpc_object_t xarray, size_t index, const uuid_t value)
{
struct xpc_object *xo, *xotmp;
xo = xarray;
xotmp = xpc_uuid_create(value);
return (xpc_array_set_value(xarray, index, xotmp));
}
void
xpc_array_set_fd(xpc_object_t xarray, size_t index, int value)
{
struct xpc_object *xo, *xotmp;
xo = xarray;
//xotmp = xpc_fd_create(value);
return (xpc_array_set_value(xarray, index, xotmp));
}
void
xpc_array_set_connection(xpc_object_t xarray, size_t index,
xpc_connection_t value)
{
}
bool
xpc_array_get_bool(xpc_object_t xarray, size_t index)
{
struct xpc_object *xotmp;
xotmp = xpc_array_get_value(xarray, index);
return (xpc_bool_get_value(xotmp));
}
int64_t
xpc_array_get_int64(xpc_object_t xarray, size_t index)
{
struct xpc_object *xotmp;
xotmp = xpc_array_get_value(xarray, index);
return (xpc_int64_get_value(xotmp));
}
uint64_t
xpc_array_get_uint64(xpc_object_t xarray, size_t index)
{
struct xpc_object *xotmp;
xotmp = xpc_array_get_value(xarray, index);
return (xpc_uint64_get_value(xotmp));
}
double
xpc_array_get_double(xpc_object_t xarray, size_t index)
{
struct xpc_object *xotmp;
xotmp = xpc_array_get_value(xarray, index);
return (xpc_double_get_value(xotmp));
}
int64_t
xpc_array_get_date(xpc_object_t xarray, size_t index)
{
struct xpc_object *xotmp;
xotmp = xpc_array_get_value(xarray, index);
return (xpc_date_get_value(xotmp));
}
const void *
xpc_array_get_data(xpc_object_t xarray, size_t index, size_t *length)
{
struct xpc_object *xotmp;
xotmp = xpc_array_get_value(xarray, index);
*length = xpc_data_get_length(xotmp);
return (xpc_data_get_bytes_ptr(xotmp));
}
const uint8_t *
xpc_array_get_uuid(xpc_object_t xarray, size_t index)
{
struct xpc_object *xotmp;
xotmp = xpc_array_get_value(xarray, index);
return (xpc_uuid_get_bytes(xotmp));
}
const char *
xpc_array_get_string(xpc_object_t xarray, size_t index)
{
struct xpc_object *xotmp;
xotmp = xpc_array_get_value(xarray, index);
return (xpc_string_get_string_ptr(xotmp));
}
int
xpc_array_dup_fd(xpc_object_t array, size_t index)
{
/* XXX */
return (-1);
}
xpc_connection_t
xpc_array_get_connection(xpc_object_t array, size_t index)
{
/* XXX */
return (NULL);
}
bool
xpc_array_apply(xpc_object_t xarray, xpc_array_applier_t applier)
{
struct xpc_object *xo, *xotmp;
struct xpc_array_head *arr;
size_t i;
i = 0;
xo = xarray;
arr = &xo->xo_array;
TAILQ_FOREACH(xotmp, arr, xo_link) {
if (!applier(i++, xotmp))
return (false);
}
return (true);
}

448
xpc_connection.c Normal file
View File

@ -0,0 +1,448 @@
/*
* Copyright 2014-2015 iXsystems, Inc.
* All rights reserved
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted providing that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*/
#include <errno.h>
#include <xpc/xpc.h>
#include <machine/atomic.h>
#include <Block.h>
#include "xpc_internal.h"
#define XPC_CONNECTION_NEXT_ID(conn) (atomic_fetchadd_long(&conn->xc_last_id, 1))
static void xpc_connection_recv_message();
static void xpc_send(xpc_connection_t xconn, xpc_object_t message, uint64_t id);
xpc_connection_t
xpc_connection_create(const char *name, dispatch_queue_t targetq)
{
char *qname;
struct xpc_transport *transport = xpc_get_transport();
struct xpc_connection *conn;
if ((conn = malloc(sizeof(struct xpc_connection))) == NULL) {
errno = ENOMEM;
return (NULL);
}
memset(conn, 0, sizeof(struct xpc_connection));
conn->xc_last_id = 1;
TAILQ_INIT(&conn->xc_peers);
TAILQ_INIT(&conn->xc_pending);
/* Create send queue */
asprintf(&qname, "com.ixsystems.xpc.connection.sendq.%p", conn);
conn->xc_send_queue = dispatch_queue_create(qname, NULL);
/* Create recv queue */
asprintf(&qname, "com.ixsystems.xpc.connection.recvq.%p", conn);
conn->xc_recv_queue = dispatch_queue_create(qname, NULL);
/* Create target queue */
conn->xc_target_queue = targetq ? targetq : dispatch_get_main_queue();
/* Receive queue is initially suspended */
dispatch_suspend(conn->xc_recv_queue);
/* Create local port */
if (transport->xt_listen(NULL, &conn->xc_local_port) != 0) {
debugf("Cannot create local port: %s", strerror(errno));
return (NULL);
}
return ((xpc_connection_t)conn);
}
xpc_connection_t
xpc_connection_create_mach_service(const char *name, dispatch_queue_t targetq,
uint64_t flags)
{
struct xpc_transport *transport = xpc_get_transport();
struct xpc_connection *conn;
conn = (struct xpc_connection *)xpc_connection_create(name, targetq);
if (conn == NULL)
return (NULL);
conn->xc_flags = flags;
if (flags & XPC_CONNECTION_MACH_SERVICE_LISTENER) {
transport->xt_release(conn->xc_local_port);
if (transport->xt_listen(name, &conn->xc_local_port) != 0) {
debugf("Cannot create local port: %s", strerror(errno));
return (NULL);
}
}
if (transport->xt_lookup(name, &conn->xc_remote_port) != 0) {
return (NULL);
}
return ((xpc_connection_t)conn);
}
xpc_connection_t
xpc_connection_create_from_endpoint(xpc_endpoint_t endpoint)
{
struct xpc_connection *conn;
conn = (struct xpc_connection *)xpc_connection_create("anonymous", NULL);
if (conn == NULL)
return (NULL);
conn->xc_remote_port = (xpc_port_t)endpoint;
return ((xpc_connection_t)conn);
}
void
xpc_connection_set_target_queue(xpc_connection_t xconn,
dispatch_queue_t targetq)
{
struct xpc_connection *conn;
debugf("connection=%p", xconn);
conn = (struct xpc_connection *)xconn;
conn->xc_target_queue = targetq;
}
void
xpc_connection_set_event_handler(xpc_connection_t xconn,
xpc_handler_t handler)
{
struct xpc_connection *conn;
debugf("connection=%p", xconn);
conn = (struct xpc_connection *)xconn;
conn->xc_handler = (xpc_handler_t)Block_copy(handler);
}
void
xpc_connection_suspend(xpc_connection_t xconn)
{
struct xpc_connection *conn;
conn = (struct xpc_connection *)xconn;
dispatch_suspend(conn->xc_recv_source);
}
void
xpc_connection_resume(xpc_connection_t xconn)
{
struct xpc_transport *transport = xpc_get_transport();
struct xpc_connection *conn;
debugf("connection=%p", xconn);
conn = (struct xpc_connection *)xconn;
/* Create dispatch source for top-level connection */
if (conn->xc_parent == NULL) {
conn->xc_recv_source = transport->xt_create_source(
conn->xc_local_port, conn->xc_recv_queue);
dispatch_set_context(conn->xc_recv_source, conn);
dispatch_source_set_event_handler_f(conn->xc_recv_source,
xpc_connection_recv_message);
dispatch_resume(conn->xc_recv_source);
}
dispatch_resume(conn->xc_recv_queue);
}
void
xpc_connection_send_message(xpc_connection_t xconn,
xpc_object_t message)
{
struct xpc_connection *conn;
uint64_t id;
conn = (struct xpc_connection *)xconn;
id = xpc_dictionary_get_uint64(message, XPC_SEQID);
if (id == 0)
id = XPC_CONNECTION_NEXT_ID(conn);
dispatch_async(conn->xc_send_queue, ^{
xpc_send(xconn, message, id);
});
}
void
xpc_connection_send_message_with_reply(xpc_connection_t xconn,
xpc_object_t message, dispatch_queue_t targetq, xpc_handler_t handler)
{
struct xpc_connection *conn;
struct xpc_pending_call *call;
conn = (struct xpc_connection *)xconn;
call = malloc(sizeof(struct xpc_pending_call));
call->xp_id = XPC_CONNECTION_NEXT_ID(conn);
call->xp_handler = handler;
call->xp_queue = targetq;
TAILQ_INSERT_TAIL(&conn->xc_pending, call, xp_link);
dispatch_async(conn->xc_send_queue, ^{
xpc_send(xconn, message, call->xp_id);
});
}
xpc_object_t
xpc_connection_send_message_with_reply_sync(xpc_connection_t conn,
xpc_object_t message)
{
__block xpc_object_t result;
dispatch_semaphore_t sem = dispatch_semaphore_create(0);
xpc_connection_send_message_with_reply(conn, message, NULL,
^(xpc_object_t o) {
result = o;
dispatch_semaphore_signal(sem);
});
dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
return (result);
}
void
xpc_connection_send_barrier(xpc_connection_t xconn, dispatch_block_t barrier)
{
struct xpc_connection *conn;
conn = (struct xpc_connection *)xconn;
dispatch_sync(conn->xc_send_queue, barrier);
}
void
xpc_connection_cancel(xpc_connection_t connection)
{
}
const char *
xpc_connection_get_name(xpc_connection_t connection)
{
return ("unknown"); /* ??? */
}
uid_t
xpc_connection_get_euid(xpc_connection_t xconn)
{
struct xpc_connection *conn;
conn = (struct xpc_connection *)xconn;
return (conn->xc_creds.xc_remote_euid);
}
gid_t
xpc_connection_get_guid(xpc_connection_t xconn)
{
struct xpc_connection *conn;
conn = (struct xpc_connection *)xconn;
return (conn->xc_creds.xc_remote_guid);
}
pid_t
xpc_connection_get_pid(xpc_connection_t xconn)
{
struct xpc_connection *conn;
conn = (struct xpc_connection *)xconn;
return (conn->xc_creds.xc_remote_pid);
}
#ifdef MACH
au_asid_t
xpc_connection_get_asid(xpc_connection_t xconn)
{
struct xpc_connection *conn;
conn = xconn;
return (conn->xc_creds.xc_remote_asid);
}
#endif
void
xpc_connection_set_context(xpc_connection_t xconn, void *ctx)
{
struct xpc_connection *conn;
conn = (struct xpc_connection *)xconn;
conn->xc_context = ctx;
}
void *
xpc_connection_get_context(xpc_connection_t xconn)
{
struct xpc_connection *conn;
conn = (struct xpc_connection *)xconn;
return (conn->xc_context);
}
void
xpc_connection_set_finalizer_f(xpc_connection_t connection,
xpc_finalizer_t finalizer)
{
}
xpc_endpoint_t
xpc_endpoint_create(xpc_connection_t connection)
{
return (NULL);
}
void
xpc_main(xpc_connection_handler_t handler)
{
dispatch_main();
}
void
xpc_transaction_begin(void)
{
}
void
xpc_transaction_end(void)
{
}
static void
xpc_send(xpc_connection_t xconn, xpc_object_t message, uint64_t id)
{
struct xpc_connection *conn;
int err;
debugf("connection=%p, message=%p, id=%lu", xconn, message, id);
conn = (struct xpc_connection *)xconn;
if (xpc_pipe_send(message, id, conn->xc_local_port,
conn->xc_remote_port) != 0)
debugf("send failed: %s", strerror(errno));
}
#ifdef MACH
static void
xpc_connection_set_credentials(struct xpc_connection *conn, audit_token_t *tok)
{
uid_t uid;
gid_t gid;
pid_t pid;
au_asid_t asid;
if (tok == NULL)
return;
audit_token_to_au32(*tok, NULL, &uid, &gid, NULL, NULL, &pid, &asid,
NULL);
conn->xc_creds.xc_remote_euid = uid;
conn->xc_creds.xc_remote_guid = gid;
conn->xc_creds.xc_remote_pid = pid;
conn->xc_creds.xc_remote_asid = asid;
}
#endif
static void
xpc_connection_recv_message(void *context)
{
struct xpc_transport *transport = xpc_get_transport();
struct xpc_pending_call *call;
struct xpc_connection *conn, *peer;
struct xpc_credentials creds;
xpc_object_t result;
xpc_port_t remote;
uint64_t id;
int err;
debugf("connection=%p", context);
conn = context;
if (xpc_pipe_receive(conn->xc_local_port, &remote, &result, &id,
&creds) != 0)
return;
debugf("message=%p, id=%lu, remote=%s", result, id,
transport->xt_port_to_string(remote));
if (conn->xc_flags & XPC_CONNECTION_MACH_SERVICE_LISTENER) {
TAILQ_FOREACH(peer, &conn->xc_peers, xc_link) {
if (transport->xt_port_compare(remote,
peer->xc_remote_port) == 0) {
dispatch_async(peer->xc_target_queue, ^{
peer->xc_handler(result);
});
return;
}
}
debugf("new peer on port %s", transport->xt_port_to_string(remote));
/* New peer */
peer = (struct xpc_connection *)xpc_connection_create(NULL, NULL);
peer->xc_parent = conn;
peer->xc_local_port = conn->xc_local_port;
peer->xc_remote_port = remote;
peer->xc_creds = creds;
TAILQ_INSERT_TAIL(&conn->xc_peers, peer, xc_link);
dispatch_async(conn->xc_target_queue, ^{
conn->xc_handler(peer);
});
dispatch_async(peer->xc_target_queue, ^{
peer->xc_handler(result);
});
} else {
conn->xc_creds = creds;
TAILQ_FOREACH(call, &conn->xc_pending, xp_link) {
if (call->xp_id == id) {
dispatch_async(conn->xc_target_queue, ^{
call->xp_handler(result);
TAILQ_REMOVE(&conn->xc_pending, call,
xp_link);
free(call);
});
return;
}
}
if (conn->xc_handler) {
dispatch_async(conn->xc_target_queue, ^{
conn->xc_handler(result);
});
}
}
}

378
xpc_dictionary.c Normal file
View File

@ -0,0 +1,378 @@
/*
* Copyright 2014-2015 iXsystems, Inc.
* All rights reserved
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted providing that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*/
#include <sys/types.h>
#include "xpc/xpc.h"
#include "xpc_internal.h"
#include "mpack.h"
#define NVLIST_XPC_TYPE "__XPC_TYPE"
struct xpc_object *
mpack2xpc(const mpack_node_t node)
{
xpc_object_t xotmp;
size_t i;
xpc_u val;
switch (mpack_node_type(node)) {
case mpack_type_nil:
xotmp = xpc_null_create();
break;
case mpack_type_int:
val.i = mpack_node_i64(node);
xotmp = xpc_int64_create(val.i);
break;
case mpack_type_uint:
val.ui = mpack_node_u64(node);
xotmp = xpc_uint64_create(val.ui);
break;
case mpack_type_bool:
val.b = mpack_node_bool(node);
xotmp = xpc_bool_create(val.b);
break;
case mpack_type_double:
val.d = mpack_node_double(node);
xotmp = xpc_double_create(val.d);
break;
case mpack_type_str:
val.str = mpack_node_cstr_alloc(node, 65536);
xotmp = xpc_string_create(val.str);
break;
case mpack_type_bin:
break;
case mpack_type_array:
xotmp = xpc_array_create(NULL, 0);
for (i = 0; i < mpack_node_array_length(node); i++) {
xpc_object_t item = mpack2xpc(
mpack_node_array_at(node, i));
xpc_array_append_value(item, xotmp);
}
break;
case mpack_type_map:
xotmp = xpc_dictionary_create(NULL, NULL, 0);
for (i = 0; i < mpack_node_map_count(node); i++) {
char *key = mpack_node_cstr_alloc(
mpack_node_map_key_at(node, i), 1024);
xpc_object_t value = mpack2xpc(
mpack_node_map_value_at(node, i));
xpc_dictionary_set_value(xotmp, key, value);
}
break;
default:
xotmp = NULL;
break;
}
return (xotmp);
}
void
xpc2mpack(mpack_writer_t *writer, xpc_object_t obj)
{
struct xpc_object *xotmp = obj;
switch (xotmp->xo_xpc_type) {
case _XPC_TYPE_DICTIONARY:
mpack_start_map(writer, xpc_dictionary_get_count(obj));
xpc_dictionary_apply(obj, ^(const char *k, xpc_object_t v) {
mpack_write_cstr(writer, k);
xpc2mpack(writer, v);
return ((bool)true);
});
mpack_finish_map(writer);
break;
case _XPC_TYPE_ARRAY:
mpack_start_array(writer, xpc_array_get_count(obj));
xpc_array_apply(obj, ^(size_t index, xpc_object_t v) {
xpc2mpack(writer, v);
return ((bool)true);
});
mpack_finish_map(writer);
break;
case _XPC_TYPE_NULL:
mpack_write_nil(writer);
break;
case _XPC_TYPE_BOOL:
mpack_write_bool(writer, xpc_bool_get_value(obj));
break;
case _XPC_TYPE_INT64:
mpack_write_i64(writer, xpc_int64_get_value(obj));
break;
case _XPC_TYPE_DOUBLE:
mpack_write_double(writer, xpc_double_get_value(obj));
case _XPC_TYPE_UINT64:
mpack_write_u64(writer, xpc_uint64_get_value(obj));
break;
case _XPC_TYPE_STRING:
mpack_write_cstr(writer, xpc_string_get_string_ptr(obj));
break;
case _XPC_TYPE_UUID:
break;
}
}
xpc_object_t
xpc_dictionary_create(const char * const *keys, const xpc_object_t *values,
size_t count)
{
struct xpc_object *xo;
size_t i;
xpc_u val;
xo = _xpc_prim_create(_XPC_TYPE_DICTIONARY, val, count);
for (i = 0; i < count; i++)
xpc_dictionary_set_value(xo, keys[i], values[i]);
return (xo);
}
xpc_object_t
xpc_dictionary_create_reply(xpc_object_t original)
{
struct xpc_object *xo, *xo_orig;
xpc_u val;
xo_orig = original;
if ((xo_orig->xo_flags & _XPC_FROM_WIRE) == 0)
return (NULL);
return xpc_dictionary_create(NULL, NULL, 0);
}
#ifdef MACH
void
xpc_dictionary_get_audit_token(xpc_object_t xdict, audit_token_t *token)
{
struct xpc_object *xo;
xo = xdict;
if (xo->xo_audit_token != NULL)
memcpy(token, xo->xo_audit_token, sizeof(*token));
}
void
xpc_dictionary_set_mach_recv(xpc_object_t xdict, const char *key,
mach_port_t port)
{
struct xpc_object *xo = xdict;
struct xpc_object *xotmp;
xpc_u val;
val.port = port;
xotmp = _xpc_prim_create(_XPC_TYPE_ENDPOINT, val, 0);
xpc_dictionary_set_value(xdict, key, xotmp);
}
void
xpc_dictionary_set_mach_send(xpc_object_t xdict, const char *key,
mach_port_t port)
{
struct xpc_object *xotmp;
xpc_u val;
val.port = port;
xotmp = _xpc_prim_create(_XPC_TYPE_ENDPOINT, val, 0);
xpc_dictionary_set_value(xdict, key, xotmp);
}
mach_port_t
xpc_dictionary_copy_mach_send(xpc_object_t xdict, const char *key)
{
struct xpc_object *xo;
const struct xpc_object *xotmp;
}
#endif
void
xpc_dictionary_set_value(xpc_object_t xdict, const char *key,
xpc_object_t value)
{
struct xpc_object *xo, *xotmp;
struct xpc_dict_head *head;
struct xpc_dict_pair *pair;
xo = xdict;
head = &xo->xo_dict;
TAILQ_FOREACH(pair, head, xo_link) {
if (!strcmp(pair->key, key)) {
pair->value = value;
return;
}
}
xo->xo_size++;
pair = malloc(sizeof(struct xpc_dict_pair));
pair->key = key;
pair->value = value;
TAILQ_INSERT_TAIL(&xo->xo_dict, pair, xo_link);
xpc_retain(value);
}
xpc_object_t
xpc_dictionary_get_value(xpc_object_t xdict, const char *key)
{
struct xpc_object *xo;
struct xpc_dict_head *head;
struct xpc_dict_pair *pair;
xo = xdict;
head = &xo->xo_dict;
TAILQ_FOREACH(pair, head, xo_link) {
if (!strcmp(pair->key, key))
return (pair->value);
}
return (NULL);
}
size_t
xpc_dictionary_get_count(xpc_object_t xdict)
{
struct xpc_object *xo;
xo = xdict;
return (xo->xo_size);
}
void
xpc_dictionary_set_bool(xpc_object_t xdict, const char *key, bool value)
{;
struct xpc_object *xo, *xotmp;
xo = xdict;
xotmp = xpc_bool_create(value);
xpc_dictionary_set_value(xdict, key, xotmp);
}
void
xpc_dictionary_set_int64(xpc_object_t xdict, const char *key, int64_t value)
{
struct xpc_object *xo, *xotmp;
xo = xdict;
xotmp = xpc_int64_create(value);
xpc_dictionary_set_value(xdict, key, xotmp);
}
void
xpc_dictionary_set_uint64(xpc_object_t xdict, const char *key, uint64_t value)
{
struct xpc_object *xo, *xotmp;
xo = xdict;
xotmp = xpc_uint64_create(value);
xpc_dictionary_set_value(xdict, key, xotmp);
}
void
xpc_dictionary_set_string(xpc_object_t xdict, const char *key,
const char *value)
{
struct xpc_object *xo, *xotmp;
xo = xdict;
xotmp = xpc_string_create(value);
xpc_dictionary_set_value(xdict, key, xotmp);
}
bool
xpc_dictionary_get_bool(xpc_object_t xdict, const char *key)
{
xpc_object_t xo;
xo = xpc_dictionary_get_value(xdict, key);
return (xpc_bool_get_value(xo));
}
int64_t
xpc_dictionary_get_int64(xpc_object_t xdict, const char *key)
{
xpc_object_t xo;
xo = xpc_dictionary_get_value(xdict, key);
return (xpc_int64_get_value(xo));
}
uint64_t
xpc_dictionary_get_uint64(xpc_object_t xdict, const char *key)
{
xpc_object_t xo;
xo = xpc_dictionary_get_value(xdict, key);
return (xpc_uint64_get_value(xo));
}
const char *
xpc_dictionary_get_string(xpc_object_t xdict, const char *key)
{
xpc_object_t xo;
xo = xpc_dictionary_get_value(xdict, key);
return (xpc_string_get_string_ptr(xo));
}
bool
xpc_dictionary_apply(xpc_object_t xdict, xpc_dictionary_applier_t applier)
{
struct xpc_object *xo, *xotmp;
struct xpc_dict_head *head;
struct xpc_dict_pair *pair;
xo = xdict;
head = &xo->xo_dict;
TAILQ_FOREACH(pair, head, xo_link) {
if (!applier(pair->key, pair->value))
return (false);
}
return (true);
}

210
xpc_internal.h Normal file
View File

@ -0,0 +1,210 @@
/*
* Copyright 2014-2015 iXsystems, Inc.
* All rights reserved
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted providing that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*/
#ifndef _LIBXPC_XPC_INTERNAL_H
#define _LIBXPC_XPC_INTERNAL_H
#include <sys/queue.h>
#include <sys/uio.h>
#include "mpack.h"
#define debugf(...) \
do { \
fprintf(stderr, "%s: ", __func__); \
fprintf(stderr, __VA_ARGS__); \
fprintf(stderr, "\n"); \
} while(0);
#define _XPC_TYPE_INVALID 0
#define _XPC_TYPE_DICTIONARY 1
#define _XPC_TYPE_ARRAY 2
#define _XPC_TYPE_BOOL 3
#define _XPC_TYPE_CONNECTION 4
#define _XPC_TYPE_ENDPOINT 5
#define _XPC_TYPE_NULL 6
#define _XPC_TYPE_INT64 8
#define _XPC_TYPE_UINT64 9
#define _XPC_TYPE_DATE 10
#define _XPC_TYPE_DATA 11
#define _XPC_TYPE_STRING 12
#define _XPC_TYPE_UUID 13
#define _XPC_TYPE_FD 14
#define _XPC_TYPE_SHMEM 15
#define _XPC_TYPE_ERROR 16
#define _XPC_TYPE_DOUBLE 17
#define _XPC_TYPE_MAX _XPC_TYPE_DOUBLE
#define XPC_SEQID "XPC sequence number"
struct xpc_object;
struct xpc_dict_pair;
struct xpc_resource;
struct xpc_credentials;
TAILQ_HEAD(xpc_dict_head, xpc_dict_pair);
TAILQ_HEAD(xpc_array_head, xpc_object);
typedef void *xpc_port_t;
typedef int (*xpc_transport_listen_t)(const char *, xpc_port_t *);
typedef int (*xpc_transport_lookup)(const char *, xpc_port_t *);
typedef char *(*xpc_transport_port_to_string)(xpc_port_t);
typedef int (*xpc_transport_port_compare)(xpc_port_t, xpc_port_t);
typedef int (*xpc_transport_release)(xpc_port_t);
typedef int (*xpc_transport_send)(xpc_port_t, xpc_port_t, struct iovec *iov,
int niov, struct xpc_resource *, size_t);
typedef int(*xpc_transport_recv)(xpc_port_t, xpc_port_t*, struct iovec *iov,
int niov, struct xpc_resource **, size_t *, struct xpc_credentials *);
typedef dispatch_source_t (*xpc_transport_create_source)(xpc_port_t,
dispatch_queue_t);
typedef union {
struct xpc_dict_head dict;
struct xpc_array_head array;
uint64_t ui;
int64_t i;
char *str;
bool b;
double d;
uintptr_t ptr;
int fd;
uuid_t uuid;
#ifdef MACH
mach_port_t port;
#endif
} xpc_u;
#define _XPC_FROM_WIRE 0x1
struct xpc_object {
uint8_t xo_xpc_type;
uint16_t xo_flags;
volatile uint32_t xo_refcnt;
size_t xo_size;
xpc_u xo_u;
#ifdef MACH
audit_token_t * xo_audit_token;
#endif
TAILQ_ENTRY(xpc_object) xo_link;
};
struct xpc_dict_pair {
const char * key;
struct xpc_object * value;
TAILQ_ENTRY(xpc_dict_pair) xo_link;
};
struct xpc_pending_call {
uint64_t xp_id;
xpc_object_t xp_response;
dispatch_queue_t xp_queue;
xpc_handler_t xp_handler;
TAILQ_ENTRY(xpc_pending_call) xp_link;
};
struct xpc_credentials {
uid_t xc_remote_euid;
gid_t xc_remote_guid;
pid_t xc_remote_pid;
#ifdef MACH
au_asid_t xc_remote_asid;
audit_token_t xc_audit_token;
#endif
};
struct xpc_connection {
const char * xc_name;
xpc_port_t xc_local_port;
xpc_port_t xc_remote_port;
xpc_handler_t xc_handler;
dispatch_source_t xc_recv_source;
dispatch_queue_t xc_send_queue;
dispatch_queue_t xc_recv_queue;
dispatch_queue_t xc_target_queue;
int xc_suspend_count;
int xc_transaction_count;
uint64_t xc_flags;
volatile uint64_t xc_last_id;
void * xc_context;
struct xpc_connection * xc_parent;
struct xpc_credentials xc_creds;
TAILQ_HEAD(, xpc_pending_call) xc_pending;
TAILQ_HEAD(, xpc_connection) xc_peers;
TAILQ_ENTRY(xpc_connection) xc_link;
};
struct xpc_resource {
int xr_type;
union {
int xr_fd;
};
};
struct xpc_transport {
const char * xt_name;
xpc_transport_listen_t xt_listen;
xpc_transport_lookup xt_lookup;
xpc_transport_port_to_string xt_port_to_string;
xpc_transport_port_compare xt_port_compare;
xpc_transport_release xt_release;
xpc_transport_send xt_send;
xpc_transport_recv xt_recv;
xpc_transport_create_source xt_create_source;
};
struct xpc_service {
xpc_port_t xs_pipe;
TAILQ_HEAD(, xpc_connection) xs_connections;
};
#define xo_str xo_u.str
#define xo_bool xo_u.b
#define xo_uint xo_u.ui
#define xo_int xo_u.i
#define xo_ptr xo_u.ptr
#define xo_d xo_u.d
#define xo_fd xo_u.fd
#define xo_uuid xo_u.uuid
#define xo_port xo_u.port
#define xo_array xo_u.array
#define xo_dict xo_u.dict
__private_extern__ struct xpc_transport *xpc_get_transport();
__private_extern__ void xpc_set_transport(struct xpc_transport *);
__private_extern__ struct xpc_object *_xpc_prim_create(int type, xpc_u value,
size_t size);
__private_extern__ struct xpc_object *_xpc_prim_create_flags(int type,
xpc_u value, size_t size, uint16_t flags);
__private_extern__ const char *_xpc_get_type_name(xpc_object_t obj);
__private_extern__ struct xpc_object *mpack2xpc(mpack_node_t node);
__private_extern__ void xpc2mpack(mpack_writer_t *writer, xpc_object_t xo);
__private_extern__ void xpc_object_destroy(struct xpc_object *xo);
__private_extern__ int xpc_pipe_send(xpc_object_t obj, uint64_t id,
xpc_port_t local, xpc_port_t remote);
__private_extern__ int xpc_pipe_receive(xpc_port_t local, xpc_port_t *remote,
xpc_object_t *result, uint64_t *id, struct xpc_credentials *creds);
#endif /* _LIBXPC_XPC_INTERNAL_H */

388
xpc_misc.c Normal file
View File

@ -0,0 +1,388 @@
/*
* Copyright 2014-2015 iXsystems, Inc.
* All rights reserved
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted providing that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*/
#include <sys/types.h>
#include <sys/errno.h>
#include <sys/sbuf.h>
#include <machine/atomic.h>
#include <assert.h>
#include <syslog.h>
#include <stdarg.h>
#include "xpc/xpc.h"
#include "xpc_internal.h"
static void xpc_copy_description_level(xpc_object_t obj, struct sbuf *sbuf,
int level);
extern struct xpc_transport unix_transport;
static struct xpc_transport *selected_transport = &unix_transport;
void
fail_log(const char *exp)
{
syslog(LOG_ERR, "%s", exp);
//sleep(1);
printf("%s", exp);
//abort();
}
struct xpc_transport *
xpc_get_transport()
{
return (selected_transport);
}
static void
xpc_dictionary_destroy(struct xpc_object *dict)
{
struct xpc_dict_head *head;
struct xpc_dict_pair *p, *ptmp;
head = &dict->xo_dict;
TAILQ_FOREACH_SAFE(p, head, xo_link, ptmp) {
TAILQ_REMOVE(head, p, xo_link);
xpc_object_destroy(p->value);
free(p);
}
}
static void
xpc_array_destroy(struct xpc_object *dict)
{
struct xpc_object *p, *ptmp;
struct xpc_array_head *head;
head = &dict->xo_array;
TAILQ_FOREACH_SAFE(p, head, xo_link, ptmp) {
TAILQ_REMOVE(head, p, xo_link);
xpc_object_destroy(p);
}
}
static int
xpc_pack(struct xpc_object *xo, void **buf, size_t *size)
{
mpack_writer_t writer;
mpack_writer_init_growable(&writer, (char **)buf, size);
xpc2mpack(&writer, xo);
if (mpack_writer_destroy(&writer) != mpack_ok)
return (-1);
return (0);
}
static struct xpc_object *
xpc_unpack(void *buf, size_t size)
{
mpack_tree_t tree;
struct xpc_object *xo;
mpack_tree_init(&tree, (const char *)buf, size);
if (mpack_tree_error(&tree) != mpack_ok) {
debugf("unpack failed: %d", mpack_tree_error(&tree))
return (NULL);
}
xo = mpack2xpc(mpack_tree_root(&tree));
return (xo);
}
void
xpc_object_destroy(struct xpc_object *xo)
{
if (xo->xo_xpc_type == _XPC_TYPE_DICTIONARY)
xpc_dictionary_destroy(xo);
if (xo->xo_xpc_type == _XPC_TYPE_ARRAY)
xpc_array_destroy(xo);
free(xo);
}
xpc_object_t
xpc_retain(xpc_object_t obj)
{
struct xpc_object *xo;
xo = obj;
atomic_add_int(&xo->xo_refcnt, 1);
return (obj);
}
void
xpc_release(xpc_object_t obj)
{
struct xpc_object *xo;
xo = obj;
if (atomic_fetchadd_int(&xo->xo_refcnt, -1) > 1)
return;
xpc_object_destroy(xo);
}
static const char *xpc_errors[] = {
"No Error Found",
"No Memory",
"Invalid Argument",
"No Such Process"
};
#if 0
const char *
xpc_strerror(int error)
{
if (error > EXMAX || error < 0)
return "BAD ERROR";
return (xpc_errors[error]);
}
#endif
char *
xpc_copy_description(xpc_object_t obj)
{
char *result;
struct sbuf *sbuf;
sbuf = sbuf_new_auto();
xpc_copy_description_level(obj, sbuf, 0);
sbuf_finish(sbuf);
result = strdup(sbuf_data(sbuf));
sbuf_delete(sbuf);
return (result);
}
static void
xpc_copy_description_level(xpc_object_t obj, struct sbuf *sbuf, int level)
{
struct xpc_object *xo = obj;
struct uuid *id;
char *uuid_str;
uint32_t uuid_status;
if (obj == NULL) {
sbuf_printf(sbuf, "<null value>\n");
return;
}
sbuf_printf(sbuf, "(%s) ", _xpc_get_type_name(obj));
switch (xo->xo_xpc_type) {
case _XPC_TYPE_DICTIONARY:
sbuf_printf(sbuf, "\n");
xpc_dictionary_apply(xo, ^(const char *k, xpc_object_t v) {
sbuf_printf(sbuf, "%*s\"%s\": ", level * 4, " ", k);
xpc_copy_description_level(v, sbuf, level + 1);
return ((bool)true);
});
break;
case _XPC_TYPE_ARRAY:
sbuf_printf(sbuf, "\n");
xpc_array_apply(xo, ^(size_t idx, xpc_object_t v) {
sbuf_printf(sbuf, "%*s%ld: ", level * 4, " ", idx);
xpc_copy_description_level(v, sbuf, level + 1);
return ((bool)true);
});
break;
case _XPC_TYPE_BOOL:
sbuf_printf(sbuf, "%s\n",
xpc_bool_get_value(obj) ? "true" : "false");
break;
case _XPC_TYPE_STRING:
sbuf_printf(sbuf, "\"%s\"\n",
xpc_string_get_string_ptr(obj));
break;
case _XPC_TYPE_INT64:
sbuf_printf(sbuf, "%ld\n",
xpc_int64_get_value(obj));
break;
case _XPC_TYPE_UINT64:
sbuf_printf(sbuf, "%lx\n",
xpc_uint64_get_value(obj));
break;
case _XPC_TYPE_DATE:
sbuf_printf(sbuf, "%lu\n",
xpc_date_get_value(obj));
break;
case _XPC_TYPE_UUID:
id = (struct uuid *)xpc_uuid_get_bytes(obj);
uuid_to_string(id, &uuid_str, &uuid_status);
sbuf_printf(sbuf, "%s\n", uuid_str);
free(uuid_str);
break;
case _XPC_TYPE_ENDPOINT:
sbuf_printf(sbuf, "<%ld>\n", xo->xo_int);
break;
case _XPC_TYPE_NULL:
sbuf_printf(sbuf, "<null>\n");
break;
}
}
#ifdef MACH
struct _launch_data {
uint64_t type;
union {
struct {
union {
launch_data_t *_array;
char *string;
void *opaque;
int64_t __junk;
};
union {
uint64_t _array_cnt;
uint64_t string_len;
uint64_t opaque_size;
};
};
int64_t fd;
uint64_t mp;
uint64_t err;
int64_t number;
uint64_t boolean; /* We'd use 'bool' but this struct needs to be used under Rosetta, and sizeof(bool) is different between PowerPC and Intel */
double float_num;
};
};
static uint8_t ld_to_xpc_type[] = {
_XPC_TYPE_INVALID,
_XPC_TYPE_DICTIONARY,
_XPC_TYPE_ARRAY,
_XPC_TYPE_FD,
_XPC_TYPE_UINT64,
_XPC_TYPE_DOUBLE,
_XPC_TYPE_BOOL,
_XPC_TYPE_STRING,
_XPC_TYPE_DATA,
_XPC_TYPE_ERROR,
_XPC_TYPE_ENDPOINT
};
xpc_object_t
ld2xpc(launch_data_t ld)
{
struct xpc_object *xo;
xpc_u val;
if (ld->type > LAUNCH_DATA_MACHPORT)
return (NULL);
if (ld->type == LAUNCH_DATA_STRING || ld->type == LAUNCH_DATA_OPAQUE) {
val.str = malloc(ld->string_len);
memcpy(__DECONST(void *, val.str), ld->string, ld->string_len);
xo = _xpc_prim_create(ld_to_xpc_type[ld->type], val, ld->string_len);
} else if (ld->type == LAUNCH_DATA_BOOL) {
val.b = (bool)ld->boolean;
xo = _xpc_prim_create(ld_to_xpc_type[ld->type], val, 0);
} else if (ld->type == LAUNCH_DATA_ARRAY) {
xo = xpc_array_create(NULL, 0);
for (uint64_t i = 0; i < ld->_array_cnt; i++)
xpc_array_append_value(xo, ld2xpc(ld->_array[i]));
} else {
val.ui = ld->mp;
xo = _xpc_prim_create(ld_to_xpc_type[ld->type], val, ld->string_len);
}
return (xo);
}
#endif
#if 0
xpc_object_t
xpc_copy_entitlement_for_token(const char *key __unused, audit_token_t *token __unused)
{
xpc_u val;
val.b = true;
return (_xpc_prim_create(_XPC_TYPE_BOOL, val,0));
}
#endif
int
xpc_pipe_send(xpc_object_t xobj, uint64_t id, xpc_port_t local, xpc_port_t remote)
{
struct xpc_transport *transport = xpc_get_transport();
struct iovec iov[2];
assert(xpc_get_type(xobj) == &_xpc_type_dictionary);
iov[0].iov_base = (void *)&id;
iov[0].iov_len = sizeof(uint64_t);
if (xpc_pack(xobj, &iov[1].iov_base, &iov[1].iov_len) != 0) {
debugf("pack failed");
return (-1);
}
if (transport->xt_send(local, remote, (struct iovec *)&iov, 2, NULL, 0) != 0) {
debugf("transport send function failed: %s", strerror(errno));
return (-1);
}
return (0);
}
int
xpc_pipe_receive(xpc_port_t local, xpc_port_t *remote, xpc_object_t *result,
uint64_t *id, struct xpc_credentials *creds)
{
struct xpc_transport *transport = xpc_get_transport();
struct xpc_resource *resources;
struct iovec iov;
size_t nresources;
iov.iov_base = malloc(65535);
iov.iov_len = 65535;
if (transport->xt_recv(local, remote, &iov, 1, &resources, &nresources, creds) != 0) {
debugf("transport receive function failed: %s", strerror(errno));
return (-1);
}
*id = *(uint64_t *)iov.iov_base;
*result = xpc_unpack(iov.iov_base + sizeof(uint64_t), iov.iov_len - sizeof(uint64_t));
if (*result == NULL)
return (-1);
return (0);
}

492
xpc_type.c Normal file
View File

@ -0,0 +1,492 @@
/*
* Copyright 2014-2015 iXsystems, Inc.
* All rights reserved
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted providing that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*/
#include <sys/types.h>
#include <time.h>
#include "xpc/xpc.h"
#include "xpc_internal.h"
struct _xpc_type_s {
};
typedef const struct _xpc_type_s xt;
xt _xpc_type_array;
xt _xpc_type_bool;
xt _xpc_type_connection;
xt _xpc_type_data;
xt _xpc_type_date;
xt _xpc_type_dictionary;
xt _xpc_type_endpoint;
xt _xpc_type_null;
xt _xpc_type_error;
xt _xpc_type_fd;
xt _xpc_type_int64;
xt _xpc_type_uint64;
xt _xpc_type_shmem;
xt _xpc_type_string;
xt _xpc_type_uuid;
xt _xpc_type_double;
struct _xpc_bool_s {
};
typedef const struct _xpc_bool_s xb;
xb _xpc_bool_true;
xb _xpc_bool_false;
static size_t xpc_data_hash(const uint8_t *data, size_t length);
static xpc_type_t xpc_typemap[] = {
NULL,
XPC_TYPE_DICTIONARY,
XPC_TYPE_ARRAY,
XPC_TYPE_BOOL,
XPC_TYPE_CONNECTION,
XPC_TYPE_ENDPOINT,
XPC_TYPE_NULL,
NULL,
XPC_TYPE_INT64,
XPC_TYPE_UINT64,
XPC_TYPE_DATE,
XPC_TYPE_DATA,
XPC_TYPE_STRING,
XPC_TYPE_UUID,
XPC_TYPE_FD,
XPC_TYPE_SHMEM,
XPC_TYPE_ERROR,
XPC_TYPE_DOUBLE
};
static const char *xpc_typestr[] = {
"invalid",
"dictionary",
"array",
"bool",
"connection",
"endpoint",
"null",
"invalid",
"int64",
"uint64",
"date",
"data",
"string",
"uuid",
"fd",
"shmem",
"error",
"double"
};
__private_extern__ struct xpc_object *
_xpc_prim_create(int type, xpc_u value, size_t size)
{
return (_xpc_prim_create_flags(type, value, size, 0));
}
__private_extern__ struct xpc_object *
_xpc_prim_create_flags(int type, xpc_u value, size_t size, uint16_t flags)
{
struct xpc_object *xo;
if ((xo = malloc(sizeof(*xo))) == NULL)
return (NULL);
xo->xo_size = size;
xo->xo_xpc_type = type;
xo->xo_flags = flags;
xo->xo_u = value;
xo->xo_refcnt = 1;
#if MACH
xo->xo_audit_token = NULL;
#endif
if (type == _XPC_TYPE_DICTIONARY)
TAILQ_INIT(&xo->xo_dict);
if (type == _XPC_TYPE_ARRAY)
TAILQ_INIT(&xo->xo_array);
return (xo);
}
xpc_object_t
xpc_null_create(void)
{
xpc_u val;
return _xpc_prim_create(_XPC_TYPE_NULL, val, 0);
}
xpc_object_t
xpc_bool_create(bool value)
{
xpc_u val;
val.b = value;
return _xpc_prim_create(_XPC_TYPE_BOOL, val, 1);
}
bool
xpc_bool_get_value(xpc_object_t xbool)
{
struct xpc_object *xo;
xo = xbool;
if (xo == NULL)
return (0);
if (xo->xo_xpc_type == _XPC_TYPE_BOOL)
return (xo->xo_bool);
return (false);
}
xpc_object_t
xpc_int64_create(int64_t value)
{
xpc_u val;
val.i = value;
return _xpc_prim_create(_XPC_TYPE_INT64, val, 1);
}
int64_t
xpc_int64_get_value(xpc_object_t xint)
{
struct xpc_object *xo;
xo = xint;
if (xo == NULL)
return (0);
if (xo->xo_xpc_type == _XPC_TYPE_INT64)
return (xo->xo_int);
return (0);
}
xpc_object_t
xpc_uint64_create(uint64_t value)
{
xpc_u val;
val.ui = value;
return _xpc_prim_create(_XPC_TYPE_UINT64, val, 1);
}
uint64_t
xpc_uint64_get_value(xpc_object_t xuint)
{
struct xpc_object *xo;
xo = xuint;
if (xo == NULL)
return (0);
if (xo->xo_xpc_type == _XPC_TYPE_UINT64)
return (xo->xo_uint);
return (0);
}
xpc_object_t
xpc_double_create(double value)
{
xpc_u val;
val.d = value;
return _xpc_prim_create(_XPC_TYPE_DOUBLE, val, 1);
}
double
xpc_double_get_value(xpc_object_t xdouble)
{
struct xpc_object *xo = xdouble;
if (xo->xo_xpc_type == _XPC_TYPE_DOUBLE)
return (xo->xo_d);
return (0);
}
xpc_object_t
xpc_date_create(int64_t interval)
{
xpc_u val;
val.i = interval;
return _xpc_prim_create(_XPC_TYPE_DATE, val, 1);
}
xpc_object_t
xpc_date_create_from_current(void)
{
xpc_u val;
struct timespec tp;
clock_gettime(CLOCK_REALTIME, &tp);
val.ui = *(uint64_t *)&tp;
return _xpc_prim_create(_XPC_TYPE_DATE, val, 1);
}
int64_t
xpc_date_get_value(xpc_object_t xdate)
{
struct xpc_object *xo = xdate;
if (xo == NULL)
return (0);
if (xo->xo_xpc_type == _XPC_TYPE_DATE)
return (xo->xo_int);
return (0);
}
xpc_object_t
xpc_data_create(const void *bytes, size_t length)
{
xpc_u val;
val.ptr = (uintptr_t)bytes;
return _xpc_prim_create(_XPC_TYPE_DATA, val, length);
}
#ifdef MACH
xpc_object_t
xpc_data_create_with_dispatch_data(dispatch_data_t ddata)
{
}
#endif
size_t
xpc_data_get_length(xpc_object_t xdata)
{
struct xpc_object *xo = xdata;
if (xo == NULL)
return (0);
if (xo->xo_xpc_type == _XPC_TYPE_DATA)
return (xo->xo_size);
return (0);
}
const void *
xpc_data_get_bytes_ptr(xpc_object_t xdata)
{
struct xpc_object *xo = xdata;
if (xo == NULL)
return (NULL);
if (xo->xo_xpc_type == _XPC_TYPE_DATA)
return ((const void *)xo->xo_ptr);
return (0);
}
size_t
xpc_data_get_bytes(xpc_object_t xdata, void *buffer, size_t off, size_t length)
{
/* XXX */
return (0);
}
xpc_object_t
xpc_string_create(const char *string)
{
xpc_u val;
val.str = __DECONST(char *, string);
return _xpc_prim_create(_XPC_TYPE_STRING, val, strlen(string));
}
xpc_object_t
xpc_string_create_with_format(const char *fmt, ...)
{
va_list ap;
xpc_u val;
va_start(ap, fmt);
vasprintf(&val.str, fmt, ap);
va_end(ap);
return _xpc_prim_create(_XPC_TYPE_STRING, val, strlen(val.str));
}
xpc_object_t
xpc_string_create_with_format_and_arguments(const char *fmt, va_list ap)
{
xpc_u val;
vasprintf(&val.str, fmt, ap);
return _xpc_prim_create(_XPC_TYPE_STRING, val, strlen(val.str));
}
size_t
xpc_string_get_length(xpc_object_t xstring)
{
struct xpc_object *xo = xstring;
if (xo == NULL)
return (0);
if (xo->xo_xpc_type == _XPC_TYPE_STRING)
return (xo->xo_size);
return (0);
}
const char *
xpc_string_get_string_ptr(xpc_object_t xstring)
{
struct xpc_object *xo = xstring;
if (xo == NULL)
return (NULL);
if (xo->xo_xpc_type == _XPC_TYPE_STRING)
return (xo->xo_str);
return (NULL);
}
xpc_object_t
xpc_uuid_create(const uuid_t uuid)
{
xpc_u val;
memcpy(val.uuid, uuid, sizeof(uuid_t));
return _xpc_prim_create(_XPC_TYPE_UUID, val, 1);
}
const uint8_t *
xpc_uuid_get_bytes(xpc_object_t xuuid)
{
struct xpc_object *xo;
xo = xuuid;
if (xo == NULL)
return (NULL);
if (xo->xo_xpc_type == _XPC_TYPE_UUID)
return ((uint8_t*)&xo->xo_uuid);
return (NULL);
}
xpc_type_t
xpc_get_type(xpc_object_t obj)
{
struct xpc_object *xo;
xo = obj;
return (xpc_typemap[xo->xo_xpc_type]);
}
bool
xpc_equal(xpc_object_t x1, xpc_object_t x2)
{
struct xpc_object *xo1, *xo2;
xo1 = x1;
xo2 = x2;
/* FIXME */
return (false);
}
static size_t
xpc_data_hash(const uint8_t *data, size_t length)
{
size_t hash = 5381;
while (length--)
hash = ((hash << 5) + hash) + data[length];
return (hash);
}
size_t
xpc_hash(xpc_object_t obj)
{
struct xpc_object *xo;
__block size_t hash = 0;
xo = obj;
switch (xo->xo_xpc_type) {
case _XPC_TYPE_BOOL:
case _XPC_TYPE_INT64:
case _XPC_TYPE_UINT64:
case _XPC_TYPE_DATE:
case _XPC_TYPE_ENDPOINT:
return ((size_t)xo->xo_u.ui);
case _XPC_TYPE_STRING:
return (xpc_data_hash(
(const uint8_t *)xpc_string_get_string_ptr(obj),
xpc_string_get_length(obj)));
case _XPC_TYPE_DATA:
return (xpc_data_hash(
xpc_data_get_bytes_ptr(obj),
xpc_data_get_length(obj)));
case _XPC_TYPE_DICTIONARY:
xpc_dictionary_apply(obj, ^(const char *k, xpc_object_t v) {
hash ^= xpc_data_hash((const uint8_t *)k, strlen(k));
hash ^= xpc_hash(v);
return ((bool)true);
});
return (hash);
case _XPC_TYPE_ARRAY:
xpc_array_apply(obj, ^(size_t idx, xpc_object_t v) {
hash ^= xpc_hash(v);
return ((bool)true);
});
return (hash);
}
return (0);
}
__private_extern__ const char *
_xpc_get_type_name(xpc_object_t obj)
{
struct xpc_object *xo;
xo = obj;
return (xpc_typestr[xo->xo_xpc_type]);
}